package arkham.gui;

import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.*;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;

import org.japura.gui.CheckList;
import org.japura.gui.event.ListCheckListener;
import org.japura.gui.event.ListEvent;
import org.japura.gui.model.DefaultListCheckModel;
import org.japura.gui.model.ListCheckModel;
import org.japura.gui.renderer.CheckListRenderer;

import arkham.data.*;
import arkham.data.Globals.InvestigatorSkill;
import arkham.data.Investigator.InvestigatorPower;
import arkham.jaxb.encounter.EventNode;
import arkham.util.AHLogger;
import arkham.util.ImageUtils;
import arkham.util.XmlTools;

import java.util.List;
import java.util.ArrayList;

/**
 * This class manages the display of the view.
 *
 * @author ctheng
 *
 */
public class ViewManager {

    public static final int ACTION_INVESTIGATOR_SELECTION = 1;
    public static final int PHASE_PANEL_WIDTH = 65;
    public static final int ACTION_LISTCELL_WIDTH = 140;
    public static final int ACTION_LIST_WIDTH = 155;
    public static final int ACTION_LIST_HEIGHT = 20;

    private GameData gameData;
    private Font arkhamFont, gothicFont, prestigeFont, kingFont;
    private JPanel infoPanel, mainPanel, phasePanel;
    private JLayeredPane layeredPane;
    private ResizableImagePanel globalPanel, mapPanel, mouseOverPanel;

    /* View components for the main panel */
    private BoardManager boardManager;
    private int mapWidth, mapHeight;
    private ResizableImagePanel[] playerPanel;
    private JPanel actionPanel;
    private SlidingPanel slidingPanel;

    /* View components for the info panel */
    private ArkhamInfoPane infoDisplay;
    private JTabbedPane investigatorPanel;
    private Investigator displayedInvestigator;
    private JPanel personalPane, inventoryPane, effectsPane;

    /* View components for the tabbed panes */
    private JLabel persPaneName, persPaneProfession, persPaneStatus, invenPaneName, effectsPaneName;
    private JList powerList, inventoryList, effectList, monsterTrophyList, gateTrophyList;
    private DefaultListModel powerListModel, inventoryListModel, effectListModel;
    private DefaultListModel monsterTrophyListModel, gateTrophyListModel;

    /* View components for the player panels */
    private JLabel[] stamDisplay, sanDisplay, ownerDisplay, invNameDisplay;
    private JLabel[] clueDisplay, moneyDisplay;
    private JPanel[] pEffectPanel;
    private JTextPane[] skillDisplay;

    /* View components for the global panel */
    private JLabel aoName, doomTrack, terrorTrack, currentEvent;
    private JPanel globalEffectPanel;
    private BufferedImage doomIcon, terrorIcon, weatherIcon, clueIcon, moneyIcon;
    private BufferedImage personIcon, bagIcon, effectsIcon;

    /* View components for the phase panel */
    private JTextArea phaseDisplay;
    private ResizableImagePanel phaseImage;
    private BufferedImage phaseBackground;
    private BufferedImage[] phaseProgress;

    /* View components for the action panel */
    private int cardListChoices, handsFree;
    private Investigator actionPanelInv;
    private Monster combatPanelMon;
    private JLabel actionTitle, activePlayerDisplay, focusDisplay, moveRemainDisplay;
    private JLabel monsterNameDisplay, handsFreeDisplay;
    private JPanel cardPanel, cardChoiceButtonsPane;
    private JButton actionOkButton, actionCancelButton, rollClueButton, rollEffectButton;
    private JButton moveEffectButton, moveTradeButton, combatEvadeButton, combatEffectButton;
    private JTextArea helpString;
    private JComboBox actionList, rollPanelList, movePanelList, tradeRecipientList, combatPanelList;
    private JList invCardList, cardChoiceList, diceList, equippedList;
    private CheckList weaponsList;
    private ListCheckModel tradeListModel1, tradeListModel2, acceptListModel1, acceptListModel2;
    private ListCheckModel weaponsListModel;
    private DefaultListModel invCardListModel, cardChoiceListModel, diceDisplayModel,
        equippedListModel;
    private DefaultComboBoxModel actionListModel, rollPanelListModel, movePanelListModel,
        combatPanelListModel;
    private List<EventNode> encounterList;
    private String owLocation;
    private BufferedImage[] diceIcons;
    private JTextPane[] skillSliderDisplays;
    private JSlider[] skillSliders;

    public ViewManager(GameData gdata) {
        this.gameData = gdata;
        this.arkhamFont = ImageUtils.loadFont("fonts/myoldrem.ttf").deriveFont(
                Font.PLAIN, 20);
        gothicFont = ImageUtils.loadFont("fonts/gothikka.ttf").deriveFont(
                Font.BOLD, 20);
        prestigeFont = ImageUtils.loadFont("fonts/prestige.ttf").deriveFont(
                Font.PLAIN, 14);
        kingFont = ImageUtils.loadFont("fonts/kingthings.ttf").deriveFont(
                Font.PLAIN, 18);
        init();
    }

    private void init() {
        loadImages();
        initInfoPanel();
        initMainPanel();
        initGlobalPanel();
        initPhasePanel();
    }

    private void loadImages() {
        doomIcon = ImageUtils.loadImage("img/icons/doomtoken.png");
        doomIcon = ImageUtils.resizeImage(doomIcon, 40, 40);
        terrorIcon = ImageUtils.loadImage("img/icons/terrortoken.png");
        terrorIcon = ImageUtils.resizeImage(terrorIcon, 35, 45);
        weatherIcon = ImageUtils.loadImage("img/icons/weather.png");
        clueIcon = ImageUtils.loadImage("img/icons/cluetoken.png");
        moneyIcon = ImageUtils.loadImage("img/icons/money.png");
        personIcon = ImageUtils.loadImage("img/icons/personal.png");
        personIcon = ImageUtils.resizeImage(personIcon, 30, 30);
        bagIcon = ImageUtils.loadImage("img/icons/inventory.png");
        bagIcon = ImageUtils.resizeImage(bagIcon, 30, 30);
        effectsIcon = ImageUtils.loadImage("img/icons/peffects.png");
        effectsIcon = ImageUtils.resizeImage(effectsIcon, 30, 30);
        diceIcons = new BufferedImage[6];
        for (int i = 0; i < 6; i++)
        {
            diceIcons[i] = ImageUtils.loadImage("img/icons/dice" + (i + 1) + ".png");
        }
        phaseBackground = ImageUtils.loadImage("img/phaseBackground.png");
        phaseProgress = new BufferedImage[6];
        for (int i = 0; i < 6; i++)
        {
            phaseProgress[i] = ImageUtils.loadImage("img/progressbar" + i + ".png");
        }
    }

    /**
     * Initializes the main panel on the left side of the screen.
     */
    private void initMainPanel() {

        mainPanel = new JPanel();
        mainPanel.setName("Main Panel");
        mainPanel.setLayout(new GridBagLayout());
        GridBagConstraints gc = new GridBagConstraints();
        // init player panels;
        int np = gameData.getNumPlayers();
        playerPanel = new ResizableImagePanel[np];
        ownerDisplay = new JLabel[np];
        invNameDisplay = new JLabel[np];
        stamDisplay = new JLabel[np];
        sanDisplay = new JLabel[np];
        clueDisplay = new JLabel[np];
        moneyDisplay = new JLabel[np];
        pEffectPanel = new JPanel[np];
        skillDisplay = new JTextPane[np];
        mapWidth = 800 + 250 * gameData.getExpansionsInPlay();
        mapHeight = 450;
        for (int i = 0; i < np; i++) {
            playerPanel[i] = new ResizableImagePanel(
                    ImageUtils.createBlankImage(mapWidth/4, 90, new Color(240, 240, 240)));
            initPlayerPanel(i);
            ImageUtils.setFixedSize(playerPanel[i], mapWidth / 4, 90);
        }
        // init map
        boardManager = new BoardManager(mapWidth, mapHeight, gameData);
        mapPanel = new ResizableImagePanel(boardManager.getBoardImage());
        mouseOverPanel = new ResizableImagePanel(boardManager.getMouseoverImage());
        mapPanel.setName("Map Panel");
        mapPanel.add(mouseOverPanel);
        mapPanel.setBounds(0, 0, mapWidth, mapHeight);
        mouseOverPanel.setBounds(0, 0, mapWidth, mapHeight);
        initActionPanel();
        slidingPanel = new SlidingPanel(SlidingPanel.SLIDE_LEFT, mapWidth, 0, actionPanel);

        layeredPane = new JLayeredPane();
        layeredPane.setName("Layered Pane");
        layeredPane.add(mapPanel, new Integer(1));
        layeredPane.add(slidingPanel, new Integer(2));
        if (np <= 4) {
            // if less than 4 players, use the top row only
            for (int i = 0; i < np; i++) {
                ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.25, 0,
                        i, 0);
                mainPanel.add(playerPanel[i], gc);
            }
            for (int i = np; i < 4; i++)
            {
                ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.25, 0,
                        i, 0);
                mainPanel.add(Box.createRigidArea(
                        new Dimension(mapWidth / 4, 90)));
            }
            ImageUtils.initGBC(gc, GridBagConstraints.BOTH, 1, 1, 0, 1);
            gc.gridwidth = 4;
            mainPanel.add(layeredPane, gc);
        } else {
            for (int i = 0; i < 4; i++) {
                ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.25, 0,
                        i, 0);
                mainPanel.add(playerPanel[i], gc);
            }
            ImageUtils.initGBC(gc, GridBagConstraints.BOTH, 1, 1, 0, 1);
            gc.gridwidth = 4;
            mainPanel.add(layeredPane, gc);
            gc.gridwidth = 1;
            for (int i = 0; i < np - 4; i++) {
                ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.25, 0,
                        3 - i, 2);
                mainPanel.add(playerPanel[i + 4], gc);
            }
        }

        updateMainPanel();
    }

    private void initPlayerPanel(int index)
    {
        playerPanel[index].setName("Player Panel/" + index);
        playerPanel[index].setLayout(new BoxLayout(playerPanel[index], BoxLayout.X_AXIS));
        // inv name display
        invNameDisplay[index] = new JLabel("", JLabel.CENTER);
        invNameDisplay[index].setFont(arkhamFont.deriveFont(Font.PLAIN, 18));
        invNameDisplay[index].setAlignmentX(Component.CENTER_ALIGNMENT);
        // owner display
        ownerDisplay[index] = new JLabel("", JLabel.CENTER);
        ownerDisplay[index].setFont(new Font("Arial", Font.BOLD, 12));
        ownerDisplay[index].setAlignmentX(Component.CENTER_ALIGNMENT);
        // stamina and sanity display
        stamDisplay[index] = new JLabel("", JLabel.CENTER);
        sanDisplay[index] = new JLabel("", JLabel.CENTER);
        stamDisplay[index].setFont(arkhamFont);
        stamDisplay[index].setForeground(Color.red);
        JLabel slash = new JLabel("/", JLabel.CENTER);
        slash.setFont(arkhamFont);
        slash.setForeground(Color.black);
        sanDisplay[index].setFont(arkhamFont);
        sanDisplay[index].setForeground(Color.blue);
        JPanel statDisplay = new JPanel();
        statDisplay.setLayout(new BoxLayout(statDisplay, BoxLayout.X_AXIS));
        statDisplay.setAlignmentX(Component.CENTER_ALIGNMENT);
        statDisplay.add(Box.createHorizontalGlue());
        statDisplay.add(stamDisplay[index]);
        statDisplay.add(slash);
        statDisplay.add(sanDisplay[index]);
        statDisplay.add(Box.createHorizontalGlue());
        // player effects panel
        pEffectPanel[index] = new JPanel();
        pEffectPanel[index].setLayout(new BoxLayout(pEffectPanel[index], BoxLayout.X_AXIS));
        pEffectPanel[index].setBorder(BorderFactory.createEmptyBorder());
        pEffectPanel[index].setAlignmentX(Component.CENTER_ALIGNMENT);
        JScrollPane pEffectScroller = new JScrollPane(pEffectPanel[index]);
        pEffectScroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
        pEffectScroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        pEffectScroller.setAlignmentX(Component.CENTER_ALIGNMENT);
        pEffectScroller.setBorder(BorderFactory.createEmptyBorder());
        ImageUtils.setFixedSize(pEffectScroller, 90, 40);
        JPanel combinedPane = new JPanel();
        combinedPane.setLayout(new BoxLayout(combinedPane, BoxLayout.Y_AXIS));
        combinedPane.add(invNameDisplay[index]);
        combinedPane.add(ownerDisplay[index]);
        combinedPane.add(pEffectScroller);
        combinedPane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
        // count panel
        moneyDisplay[index] = new JLabel("", JLabel.CENTER);
        moneyDisplay[index].setFont(arkhamFont);
        clueDisplay[index] = new JLabel("", JLabel.CENTER);
        clueDisplay[index].setFont(arkhamFont);
        JPanel countPane = new JPanel();
        countPane.setLayout(new GridBagLayout());
        GridBagConstraints gc = new GridBagConstraints();
        ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 1, 0.33, 0, 0);
        gc.gridwidth = 2;
        countPane.add(statDisplay, gc);
        gc.gridwidth = 1;
        ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0, 0.33, 0, 1);
        countPane.add(new ImageLabel(clueIcon, "Clues"), gc);
        ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 1, 0.33, 1, 1);
        countPane.add(clueDisplay[index], gc);
        ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0, 0.33, 0, 2);
        countPane.add(new ImageLabel(moneyIcon, "Money"), gc);
        ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 1, 0.33, 1, 2);
        countPane.add(moneyDisplay[index], gc);

        // skill panel
        skillDisplay[index] = new JTextPane();
        StyledDocument styledDoc = skillDisplay[index].getStyledDocument();
        Style def = StyleContext.getDefaultStyleContext().
            getStyle(StyleContext.DEFAULT_STYLE);
        Style regular = styledDoc.addStyle("regular", def);
        StyleConstants.setFontFamily(def, "Arial");
        StyleConstants.setLineSpacing(regular, 0.1f);
        StyleConstants.setFontFamily(regular, prestigeFont.getFamily());
        StyleConstants.setFontSize(regular, prestigeFont.getSize());
        Style redblack = styledDoc.addStyle("redblack", regular);
        StyleConstants.setBackground(redblack, new Color(249, 232, 214));
        StyleConstants.setForeground(redblack, Color.black);
        Style red = styledDoc.addStyle("red", redblack);
        StyleConstants.setBackground(red, new Color(249, 232, 214));
        StyleConstants.setForeground(red, new Color(197, 67, 79));
        StyleConstants.setBold(red, true);
        Style blueblack = styledDoc.addStyle("blueblack", regular);
        StyleConstants.setBackground(blueblack, new Color(221, 226 ,227));
        StyleConstants.setForeground(blueblack, Color.black);
        Style blue = styledDoc.addStyle("blue", blueblack);
        StyleConstants.setBackground(blue, new Color(221, 226 ,227));
        StyleConstants.setForeground(blue, new Color(0, 110, 180));
        StyleConstants.setBold(blue, true);
        skillDisplay[index].setParagraphAttributes(regular, false);
        skillDisplay[index].setFont(prestigeFont);
        skillDisplay[index].setBorder(null);
        skillDisplay[index].setEditable(false);
        skillDisplay[index].setCursor(null);
        skillDisplay[index].setOpaque(false);
        skillDisplay[index].setFocusable(false);

        playerPanel[index].setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
        playerPanel[index].add(combinedPane);
        playerPanel[index].add(Box.createHorizontalStrut(5));
        playerPanel[index].add(countPane);
        playerPanel[index].add(Box.createHorizontalStrut(5));
        playerPanel[index].add(skillDisplay[index]);
        ImageUtils.setChildOpacity(playerPanel[index], false);

    }

    /**
     * Initializes the detailed info panel, at the top right
     */
    private void initInfoPanel()
    {
        infoPanel = new JPanel();
        infoPanel.setName("Info Panel");
        infoPanel.setBorder(BorderFactory.createEmptyBorder());
        infoPanel.setMinimumSize(new Dimension(160, 400));
        infoPanel.setPreferredSize(new Dimension(160, 400));
        infoPanel.setMaximumSize(new Dimension(160, 1000));
        initInvPanelTabs();
        JScrollPane jsp1 = new JScrollPane(personalPane);
        JScrollPane jsp2 = new JScrollPane(inventoryPane);
        JScrollPane jsp3 = new JScrollPane(effectsPane);
        jsp1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        jsp1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        jsp2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        jsp2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        jsp3.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        jsp3.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        /* Initialize the investigator panel and add the 3 info panels */
        investigatorPanel = new JTabbedPane(JTabbedPane.BOTTOM);
        investigatorPanel.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
        investigatorPanel.addTab(null, jsp1);
        investigatorPanel.addTab(null, jsp2);
        investigatorPanel.addTab(null, jsp3);
        investigatorPanel.setTabComponentAt(0, new ImageLabel(personIcon));
        investigatorPanel.setToolTipTextAt(0, "Personal Info");
        investigatorPanel.setTabComponentAt(1, new ImageLabel(bagIcon));
        investigatorPanel.setToolTipTextAt(1, "Inventory");
        investigatorPanel.setTabComponentAt(2, new ImageLabel(effectsIcon));
        investigatorPanel.setToolTipTextAt(2, "Effects");
        // initialize info display
        infoDisplay = new ArkhamInfoPane();
        JScrollPane jsp4 = new JScrollPane(infoDisplay);
        jsp4.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.black),
                BorderFactory.createBevelBorder(BevelBorder.RAISED)));
        jsp4.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        jsp4.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        // combine it all
        infoPanel.setLayout(new GridLayout(2, 1));
        infoPanel.add(investigatorPanel);
        infoPanel.add(jsp4);
    }

    /**
     * Initialize the tabs in the investigator panel
     */
    @SuppressWarnings("serial")
    private void initInvPanelTabs()
    {
        // personal tab
        personalPane = new JPanel();
        personalPane.setLayout(new BoxLayout(personalPane, BoxLayout.Y_AXIS));
        persPaneName = new JLabel("", JLabel.CENTER);
        persPaneName.setFont(arkhamFont);
        persPaneName.setAlignmentX(Component.CENTER_ALIGNMENT);
        persPaneName.setBorder(BorderFactory.createEmptyBorder());
        invenPaneName = new JLabel("", JLabel.CENTER);
        invenPaneName.setFont(arkhamFont);
        invenPaneName.setAlignmentX(Component.CENTER_ALIGNMENT);
        invenPaneName.setBorder(BorderFactory.createEmptyBorder());
        effectsPaneName = new JLabel("", JLabel.CENTER);
        effectsPaneName.setFont(arkhamFont);
        effectsPaneName.setAlignmentX(Component.CENTER_ALIGNMENT);
        effectsPaneName.setBorder(BorderFactory.createEmptyBorder());
        persPaneProfession = new JLabel("", JLabel.CENTER);
        persPaneProfession.setFont(arkhamFont);
        persPaneProfession.setAlignmentX(Component.CENTER_ALIGNMENT);
        persPaneProfession.setBorder(BorderFactory.createEmptyBorder());
        persPaneStatus = new JLabel("", JLabel.CENTER);
        persPaneStatus.setFont(arkhamFont.deriveFont(Font.PLAIN, 16));
        persPaneStatus.setAlignmentX(Component.CENTER_ALIGNMENT);
        persPaneStatus.setBorder(BorderFactory.createEmptyBorder());
        UnderlinedLabel powerLabel = new UnderlinedLabel("Abilities");
        powerLabel.setHorizontalAlignment(JLabel.CENTER);
        powerLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
        powerLabel.setFont(kingFont);
        powerListModel = new DefaultListModel();
        powerList = new JList(powerListModel);
        powerList.setAlignmentX(Component.CENTER_ALIGNMENT);
        powerList.setOpaque(false);
        powerList.setName("Investigator Power List");
        powerList.addMouseListener(listClickListener);
        powerList.setCellRenderer(new DefaultListCellRenderer(){

            public Component getListCellRendererComponent(JList list,
                    Object value, int index, boolean isSelected, boolean cellHasFocus) {

                if (value instanceof InvestigatorPower)
                {
                    InvestigatorPower invpower = (InvestigatorPower)value;
                    this.setText(invpower.getName());
                    if (invpower.getDescription().length() > 80)
                    {
                        this.setToolTipText(invpower.getDescription().substring(0, 80) + "...");
                    }
                    else
                    {
                        this.setToolTipText(invpower.getDescription());
                    }
                    float optimalSize = ImageUtils.calculateFontSize(
                            list, kingFont, personalPane.getWidth(), invpower.getName());
                    this.setFont(kingFont.deriveFont(Font.PLAIN, Math.min(18, optimalSize)));
                }
                this.setOpaque(false);
                return this;
            }
        });

        personalPane.add(persPaneName);
        personalPane.add(persPaneProfession);
        personalPane.add(Box.createVerticalStrut(3));
        personalPane.add(persPaneStatus);
        personalPane.add(Box.createVerticalStrut(10));
        personalPane.add(powerLabel);
        personalPane.add(Box.createVerticalStrut(3));
        personalPane.add(powerList);

        // inventory tab
        inventoryPane = new JPanel();
        inventoryPane.setLayout(new BoxLayout(inventoryPane, BoxLayout.Y_AXIS));
        inventoryListModel = new DefaultListModel();
        inventoryList = new JList(inventoryListModel);
        inventoryList.setAlignmentX(Component.CENTER_ALIGNMENT);
        inventoryList.setName("Inventory List");
        inventoryList.addMouseListener(listClickListener);
        inventoryList.setFixedCellWidth(135);
        inventoryList.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
        inventoryList.setCellRenderer(invCardCellRenderer);
        inventoryPane.add(invenPaneName);
        inventoryPane.add(Box.createVerticalStrut(2));
        inventoryPane.add(inventoryList);
        // effects tab
        effectsPane = new JPanel();
        effectsPane.setLayout(new BoxLayout(effectsPane, BoxLayout.Y_AXIS));
        ListCellRenderer lcr = new DefaultListCellRenderer()
        {
            public Component getListCellRendererComponent(JList list,
                    Object value, int index, boolean isSelected, boolean cellHasFocus)
            {
                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                Font listFont = new Font("Times New Roman", Font.PLAIN, 12);
                setFont(listFont);
                setBorder(BorderFactory.createLineBorder(Color.black));
                if (value instanceof PersonalEffect)
                {
                    PersonalEffect peff = (PersonalEffect)value;
                    int width = getFontMetrics(listFont).stringWidth(peff.getName());
                    if (width > 135)
                    {
                        StringBuffer sb = new StringBuffer(peff.getName());
                        while (width > 120 && sb.length() > 10)
                        {
                            sb.deleteCharAt(sb.length() - 1);
                            width = getFontMetrics(listFont).stringWidth(sb.toString());
                        }
                        setText(sb.toString() + "...");
                    }
                    else
                    {
                        setText(peff.getName());
                    }
                    setBackground(peff.getPreferredColor());
                }
                else if (value instanceof Monster)
                {
                    Monster mon = (Monster)value;
                    setText(mon.getName());
                    setBackground(Color.white);
                }
                else if (value instanceof GateTile)
                {
                    GateTile gate = (GateTile)value;
                    setText(gate.getName());
                    setBackground(new Color(255, 255, 206));
                }
                else if (value instanceof String)
                {
                    setText(value.toString());
                    setBackground(Color.white);
                }
                setToolTipText(this.getText());
                return this;
            }
        };
        JLabel label1 = new JLabel("Effects", JLabel.CENTER);
        label1.setAlignmentX(Component.CENTER_ALIGNMENT);
        effectListModel = new DefaultListModel();
        effectList = new JList(effectListModel);
        effectList.setAlignmentX(Component.CENTER_ALIGNMENT);
        effectList.setName("Effect List");
        effectList.addMouseListener(listClickListener);
        effectList.setFixedCellWidth(135);
        effectList.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
        effectList.setCellRenderer(lcr);

        JLabel label2 = new JLabel("Monster Trophies", JLabel.CENTER);
        label2.setAlignmentX(Component.CENTER_ALIGNMENT);
        monsterTrophyListModel = new DefaultListModel();
        monsterTrophyList = new JList(monsterTrophyListModel);
        monsterTrophyList.setName("Monster Trophy List");
        monsterTrophyList.setAlignmentX(Component.CENTER_ALIGNMENT);
        monsterTrophyList.addMouseListener(listClickListener);
        monsterTrophyList.setFixedCellWidth(135);
        monsterTrophyList.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
        monsterTrophyList.setCellRenderer(lcr);
        // The gate trophy list has no mouse listener
        JLabel label3 = new JLabel("Gate Trophies", JLabel.CENTER);
        label3.setAlignmentX(Component.CENTER_ALIGNMENT);
        gateTrophyListModel = new DefaultListModel();
        gateTrophyList = new JList(gateTrophyListModel);
        gateTrophyList.setName("Gate Trophy List");
        gateTrophyList.setAlignmentX(Component.CENTER_ALIGNMENT);
        gateTrophyList.setFixedCellWidth(135);
        gateTrophyList.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
        gateTrophyList.setCellRenderer(lcr);

        effectsPane.add(effectsPaneName);
        effectsPane.add(Box.createVerticalStrut(2));
        effectsPane.add(label1);
        effectsPane.add(effectList);
        effectsPane.add(Box.createVerticalStrut(5));
        effectsPane.add(label2);
        effectsPane.add(monsterTrophyList);
        effectsPane.add(Box.createVerticalStrut(5));
        effectsPane.add(label3);
        effectsPane.add(gateTrophyList);
    }

    /**
     * The doubleclick listener responds to double clicks on components
     */
    private MouseAdapter listClickListener = new MouseAdapter()
    {
        public void mousePressed(MouseEvent e)
        {
            String name = e.getComponent().getName();
            if (name.equals("Investigator Power List"))
            {
                int index = powerList.locationToIndex(e.getPoint());
                if (index >= 0 && index < powerListModel.getSize())
                {
                    if (powerListModel.get(index) instanceof InvestigatorPower)
                    {
                        InvestigatorPower invpower = (InvestigatorPower)powerListModel.get(index);
                        infoDisplay.displayInvestigatorPower(invpower);
                    }
                }
            }
            else if (name.equals("Inventory List"))
            {
                int index = inventoryList.locationToIndex(e.getPoint());
                if (index >= 0 && index < inventoryListModel.getSize())
                {
                    if (inventoryListModel.get(index) instanceof InvestigatorCard)
                    {
                        InvestigatorCard invcard = (InvestigatorCard)inventoryListModel.get(index);
                        infoDisplay.displayInvestigatorCard(invcard);
                    }
                }
            }
            else if (name.equals("Effect List"))
            {
                int index = effectList.locationToIndex(e.getPoint());
                if (index >= 0 && index < effectListModel.getSize())
                {
                    if (effectListModel.get(index) instanceof PersonalEffect)
                    {
                        PersonalEffect peff = (PersonalEffect)effectListModel.get(index);
                        infoDisplay.displayPersonalEffect(peff);
                    }
                }
            }
            else if (name.equals("Ancient One"))
            {
                infoDisplay.displayAncientOne(gameData.getAncientOne());
            }
            else if (name.equals("Action Card List") || name.equals("Card Choice List") ||
                    name.equals("Equipped List"))
            {
                JList list = (JList)e.getComponent();
                int index = list.locationToIndex(e.getPoint());
                if (index >= 0 && index < list.getModel().getSize())
                {
                    if (list.getModel().getElementAt(index) instanceof InvestigatorCard)
                    {
                        infoDisplay.displayInvestigatorCard((InvestigatorCard)
                                list.getModel().getElementAt(index));
                    }
                    else if (list.getModel().getElementAt(index) instanceof PersonalEffect)
                    {
                        infoDisplay.displayPersonalEffect((PersonalEffect)
                                list.getModel().getElementAt(index));
                    }
                }
            }
            else if (name.startsWith("Trade List") || name.startsWith("Accept List")
                    || name.equals("Weapons List"))
            {
                CheckList list = (CheckList)e.getComponent();
                int index = list.locationToIndex(e.getPoint());
                if (index >= 0 && index < list.getModel().getSize())
                {
                    if (list.getModel().getElementAt(index) instanceof InvestigatorCard)
                    {
                        infoDisplay.displayInvestigatorCard(
                                (InvestigatorCard)list.getModel().getElementAt(index));
                    }
                }
            }
            else if (name.equals("Monster Trophy List"))
            {
                JList list = (JList)e.getComponent();
                int index = list.locationToIndex(e.getPoint());
                if (index >= 0 && index < list.getModel().getSize())
                {
                    if (list.getModel().getElementAt(index) instanceof Monster)
                    {
                        infoDisplay.displayMonster(
                                (Monster)list.getModel().getElementAt(index));
                    }
                }
            }
            else if (name.equals("Last Mythos"))
            {
                infoDisplay.displayMythosCard(gameData.getLastDrawnMythos());
            }
        }
    };

    /**
     * Initializes the global information panel, at the bottom right
     */
    private void initGlobalPanel() {
        // note, image is 167*245
        globalPanel = new ResizableImagePanel(
                ImageUtils.loadImage("img/globalPanel.png"), true);
        globalPanel.setName("Global Panel");
        globalPanel.resizeImage(160, 200);
        ImageUtils.setFixedSize(globalPanel, 160, 200);
        globalPanel.setLayout(new BoxLayout(globalPanel, BoxLayout.Y_AXIS));
        ImageLabel doomLabel = new ImageLabel(doomIcon, "Doom Track");
        doomLabel.setBorder(BorderFactory.createEmptyBorder());
        doomLabel.setAlignmentY(Component.CENTER_ALIGNMENT);
        ImageLabel terrorLabel = new ImageLabel(terrorIcon, "Terror Track");
        terrorLabel.setBorder(BorderFactory.createEmptyBorder());
        terrorLabel.setAlignmentY(Component.CENTER_ALIGNMENT);
        aoName = new JLabel("", JLabel.CENTER);
        aoName.setFont(arkhamFont.deriveFont(Font.PLAIN, 28));
        aoName.setAlignmentX(Component.CENTER_ALIGNMENT);
        aoName.setName("Ancient One");
        aoName.addMouseListener(listClickListener);
        // doom panel
        JPanel doomPanel = new JPanel();
        doomPanel.setLayout(new BoxLayout(doomPanel, BoxLayout.X_AXIS));
        doomPanel.setOpaque(false);
        doomTrack = new JLabel("", JLabel.CENTER);
        doomTrack.setFont(arkhamFont);
        doomTrack.setForeground(Color.black);
        doomTrack.setAlignmentY(Component.CENTER_ALIGNMENT);
        doomPanel.add(doomLabel);
        doomPanel.add(Box.createHorizontalStrut(10));
        doomPanel.add(doomTrack);
        // terror panel
        JPanel terrorPanel = new JPanel();
        terrorPanel.setLayout(new BoxLayout(terrorPanel, BoxLayout.X_AXIS));
        terrorTrack = new JLabel("", JLabel.CENTER);
        terrorTrack.setFont(arkhamFont);
        terrorTrack.setAlignmentY(Component.CENTER_ALIGNMENT);
        terrorPanel.add(terrorLabel);
        terrorPanel.add(Box.createHorizontalStrut(13));
        terrorPanel.add(terrorTrack);
        terrorPanel.setOpaque(false);
        // current event
        currentEvent = new JLabel("", JLabel.CENTER);
        currentEvent.setAlignmentX(Component.CENTER_ALIGNMENT);
        currentEvent.setName("Last Mythos");
        currentEvent.addMouseListener(listClickListener);
        // global effect panel
        globalEffectPanel = new JPanel();
        globalEffectPanel.setLayout(new BoxLayout(globalEffectPanel,
                BoxLayout.X_AXIS));
        globalEffectPanel.setOpaque(false);
        globalPanel.add(Box.createVerticalStrut(10));
        globalPanel.add(aoName);
        globalPanel.add(doomPanel);
        globalPanel.add(terrorPanel);
        globalPanel.add(currentEvent);
        globalPanel.add(Box.createVerticalGlue());
        globalPanel.add(globalEffectPanel);
        globalPanel.add(Box.createVerticalStrut(5));
        updateGlobalPanel();
    }

    private void initPhasePanel()
    {
        phasePanel = new ResizableImagePanel(phaseBackground, true);
        phasePanel.setName("Phase Panel");
        phasePanel.setLayout(new BoxLayout(phasePanel, BoxLayout.Y_AXIS));
        phasePanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
        phaseDisplay = new JTextArea();
        phaseDisplay.setEditable(false);
        phaseDisplay.setFocusable(false);
        phaseDisplay.setLineWrap(true);
        phaseDisplay.setOpaque(false);
        phaseDisplay.setFont(kingFont.deriveFont(Font.PLAIN, 14));
        phaseDisplay.setForeground(Color.white);
        phaseDisplay.setBorder(BorderFactory.createEmptyBorder(0, 1, 0, 0));
        ImageUtils.setFixedSize(phaseDisplay, PHASE_PANEL_WIDTH, 70);
        phaseImage = new ResizableImagePanel(phaseProgress[0]);
        phasePanel.add(phaseDisplay);
        phasePanel.add(Box.createVerticalStrut(5));
        phasePanel.add(phaseImage);
        phasePanel.setMaximumSize(new Dimension(65, Integer.MAX_VALUE));
        phasePanel.addMouseListener(phasePanelListener);
        updatePhasePanel();
    }

    /**
     * The phase panel listener responds to clicks on the left panel by
     * showing appropriate information regarding the current phase
     */
    private MouseListener phasePanelListener = new MouseAdapter()
    {
        public void mousePressed(MouseEvent e)
        {
            e.consume();
            if (gameData.isStarted())
            {
                switch (gameData.getGamePhase())
                {
                case ARKHAM_ENCOUNTER:
                    EventNode en = gameData.getCurrentEvent();
                    if (en != null)
                    {
                        infoDisplay.displayEncounter(en.getLocation(), en.getDescription());
                    }
                    break;
                case OTHER_WORLD_ENCOUNTER:
                    en = gameData.getCurrentEvent();
                    if (en != null)
                    {
                        infoDisplay.displayEncounter(en.getLocation(), en.getDescription());
                    }
                }
            }
        }
    };

    /**
     * Initializes the action panel
     */
    private void initActionPanel()
    {
        /* Initialize data structures for action panel components */
        encounterList = new ArrayList<EventNode>();
        // action panel code
        actionPanel = new JPanel();
        ImageUtils.setFixedSize(actionPanel, 160, 449);
        actionPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
        actionTitle = new JLabel(" ", JLabel.CENTER);
        actionTitle.setFont(new Font("Times New Roman", Font.PLAIN, 18));
        actionTitle.setAlignmentX(Component.CENTER_ALIGNMENT);
        activePlayerDisplay = new JLabel("Turn: None", JLabel.CENTER);
        activePlayerDisplay.setAlignmentX(Component.CENTER_ALIGNMENT);
        helpString = new JTextArea();
        helpString.setFocusable(false);
        helpString.setEditable(false);
        helpString.setCursor(null);
        helpString.setOpaque(false);
        helpString.setLineWrap(true);
        helpString.setWrapStyleWord(true);
        helpString.setAlignmentX(Component.CENTER_ALIGNMENT);
        helpString.setText("No actions are currently available.");
        helpString.setMinimumSize(new Dimension(155, 40));
        helpString.setMaximumSize(new Dimension(155, 1000));
        /* Initialize action panel types
         * 1. Empty panel: just displays a message
         * 2. Action list panel: displays a dropdown box of choices
         * 3. Card list panel: displays a list of cards for choosing
         * 4. Card choice panel: displays a set of buttons and a list
         */

        JPanel emptyPanel = new JPanel();
        ImageUtils.setFixedSize(emptyPanel, ACTION_LIST_WIDTH, ACTION_LIST_HEIGHT);
        JPanel actionListPanel = initActionListPanel();
        JPanel cardListPanel = initCardListPanel();
        JPanel cardChoicePanel = initCardChoicePanel();
        JPanel adjustSliderPanel = initAdjustSliderPanel();
        JPanel rollPanel = initRollPanel();
        JPanel movementPanel = initMovementPanel();
        JPanel tradePanel = initTradePanel();
        JPanel tradeAcceptPanel = initTradeAcceptPanel();
        JPanel combatPanel = initCombatPanel();

        // finalize card panel
        cardPanel = new JPanel();
        CardLayout clayout = new CardLayout();
        cardPanel.setLayout(clayout);
        cardPanel.add(actionListPanel, "Action List");
        cardPanel.add(emptyPanel, "Empty Panel");
        cardPanel.add(cardListPanel, "Card List");
        cardPanel.add(cardChoicePanel, "Card Choice");
        cardPanel.add(adjustSliderPanel, "Adjust Sliders");
        cardPanel.add(rollPanel, "Roll");
        cardPanel.add(movementPanel, "Movement");
        cardPanel.add(tradePanel, "Trade");
        cardPanel.add(tradeAcceptPanel, "Trade Accept");
        cardPanel.add(combatPanel, "Combat");
        cardPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
        clayout.show(cardPanel, "Action List");

        actionOkButton = new JButton("Ok");
        actionOkButton.setActionCommand("actionOk");
        actionCancelButton = new JButton("Cancel");
        actionCancelButton.setActionCommand("actionCancel");
        actionCancelButton.setEnabled(false);

        JPanel subPanel = new JPanel();
        subPanel.setLayout(new BoxLayout(subPanel, BoxLayout.X_AXIS));
        subPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
        subPanel.add(actionOkButton);
        subPanel.add(Box.createHorizontalStrut(2));
        subPanel.add(actionCancelButton);
        actionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.Y_AXIS));
        actionPanel.add(actionTitle);
        actionPanel.add(activePlayerDisplay);
        actionPanel.add(Box.createVerticalStrut(2));
        actionPanel.add(helpString);
        actionPanel.add(Box.createVerticalStrut(2));
        actionPanel.add(cardPanel);
        actionPanel.add(subPanel);
    }

    /**
     * Initializes the action list panel in the action pane
     * @return
     */
    @SuppressWarnings("serial")
    private JPanel initActionListPanel()
    {
        // initialize action list panel
        actionListModel = new DefaultComboBoxModel();
        actionList = new JComboBox(actionListModel);
        actionList.setEditable(false);
        actionList.setRenderer(new MultilineListCellRenderer(ACTION_LISTCELL_WIDTH)
        {
            public Component getListCellRendererComponent(JList list,
                    Object value, int index, boolean isSelected, boolean cellHasFocus)
            {
                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (value instanceof NamedEntity)
                {
                    NamedEntity ne = (NamedEntity)value;
                    this.setText(ne.getName());
                }
                else if (value instanceof ModEffect)
                {
                    ModEffect mod = (ModEffect)value;
                    this.setText(mod.getDisplayString());
                }
                else if (value instanceof UsableEffect)
                {
                    UsableEffect ueff = (UsableEffect)value;
                    if (ueff.getEvent().getDescription() != null)
                    {
                        this.setText(ueff.getSource().getName() + ": " +
                                ueff.getEvent().getDescription());
                    }
                    else
                    {
                        this.setText(ueff.getSource().getName());
                    }
                }
                else if (value instanceof GateCard)
                {
                    this.setText("Gate Card " + (index + 1));
                }
                else if (value instanceof String)
                {
                    this.setText(value.toString());
                }

                setToolTipText(this.getText());
                list.setToolTipText(this.getText());
                return this;
            }
        });
        ImageUtils.setFixedSize(actionList, ACTION_LIST_WIDTH, ACTION_LIST_HEIGHT);
        JPanel actionListPanel = new JPanel();
        actionListPanel.add(actionList);
        return actionListPanel;
    }

    /**
     * Initializes the card list panel in the action pane
     * @return the card list panel
     */
    @SuppressWarnings("serial")
    private JPanel initCardListPanel()
    {
        JPanel cardListPanel = new JPanel();
        cardListPanel.setLayout(new BoxLayout(cardListPanel, BoxLayout.X_AXIS));
        invCardListModel = new DefaultListModel();
        invCardList = new JList(invCardListModel);
        invCardList.setAlignmentY(Component.TOP_ALIGNMENT);
        invCardList.setName("Action Card List");
        invCardList.addMouseListener(listClickListener);
        invCardList.setFixedCellWidth(110);
        invCardList.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createBevelBorder(BevelBorder.RAISED),
                BorderFactory.createEmptyBorder(0, 2, 0, 2)));
        invCardList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        cardListChoices = 1;
        invCardList.setCellRenderer(new DefaultListCellRenderer()
        {
            public Component getListCellRendererComponent(JList list,
                    Object value, int index, boolean isSelected, boolean cellHasFocus) {
                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (value instanceof InvestigatorCard)
                {
                    InvestigatorCard invcard = (InvestigatorCard)value;
                    setText(invcard.getName());
                    setForeground(Color.black);
                    if (index < cardListChoices)
                    {
                        setBackground(new Color(255, 163, 170));
                    }
                    else
                    {
                        setBackground(Color.white);
                    }
                    if (isSelected)
                    {
                        setBorder(BorderFactory.createLineBorder(Color.blue));
                    }
                    else
                    {
                        setBorder(null);
                    }
                }
                return this;
            }
        });
        JPanel cardListButtonPanel = new JPanel();
        cardListButtonPanel.setLayout(new BoxLayout(cardListButtonPanel, BoxLayout.Y_AXIS));
        cardListButtonPanel.setAlignmentY(Component.TOP_ALIGNMENT);
        JButton cardListUp = new JButton("^");
        JButton cardListDown = new JButton("v");
        cardListUp.setBorder(BorderFactory.createEmptyBorder(2,10,2,10));
        cardListDown.setBorder(BorderFactory.createEmptyBorder(2,10,2,10));
        cardListUp.setActionCommand("up");
        cardListDown.setActionCommand("down");
        ActionListener cardListListener = new ActionListener()
        {
            /* This listener listens for the up and down buttons and
             * shifts elements in the list when clicked
             */
            @Override
            public void actionPerformed(ActionEvent e)
            {
                int index = invCardList.getSelectedIndex();
                if (index == -1) return;
                if (e.getActionCommand().equals("up") && index > 0)
                {
                    Object temp = invCardListModel.remove(index);
                    invCardListModel.add(index - 1, temp);
                    invCardList.setSelectedIndex(index - 1);
                }
                else if (e.getActionCommand().equals("down") &&
                        index < invCardListModel.getSize() - 1)
                {
                    Object temp = invCardListModel.remove(index);
                    invCardListModel.add(index + 1, temp);
                    invCardList.setSelectedIndex(index + 1);
                }
                invCardList.repaint();
            }
        };
        cardListUp.addActionListener(cardListListener);
        cardListDown.addActionListener(cardListListener);
        cardListButtonPanel.add(cardListUp);
        cardListButtonPanel.add(Box.createVerticalStrut(2));
        cardListButtonPanel.add(cardListDown);
        cardListPanel.add(Box.createHorizontalStrut(2));
        cardListPanel.add(invCardList);
        cardListPanel.add(Box.createHorizontalStrut(2));
        cardListPanel.add(cardListButtonPanel);
        return cardListPanel;
    }

    /**
     * Initializes the card choice panel in the action pane.
     * The card choice panel consists of some buttons and a list
     * @return the card choice panel
     */
    private JPanel initCardChoicePanel()
    {
        JPanel cardChoicePanel = new JPanel();
        cardChoicePanel.setLayout(new BoxLayout(cardChoicePanel, BoxLayout.Y_AXIS));
        cardChoiceButtonsPane = new JPanel();
        cardChoiceButtonsPane.setLayout(new BoxLayout(cardChoiceButtonsPane, BoxLayout.X_AXIS));
        cardChoiceButtonsPane.setAlignmentX(Component.CENTER_ALIGNMENT);
        cardChoiceListModel = new DefaultListModel();
        cardChoiceList = new JList(cardChoiceListModel);
        cardChoiceList.setAlignmentX(Component.CENTER_ALIGNMENT);
        cardChoiceList.setAlignmentY(Component.TOP_ALIGNMENT);
        cardChoiceList.setName("Card Choice List");
        cardChoiceList.addMouseListener(listClickListener);
        cardChoiceList.setFixedCellWidth(135);
        cardChoiceList.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createBevelBorder(BevelBorder.RAISED),
                BorderFactory.createEmptyBorder(0, 2, 0, 2)));
        cardChoiceList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        cardChoicePanel.add(cardChoiceButtonsPane);
        cardChoicePanel.add(cardChoiceList);
        cardChoicePanel.add(Box.createVerticalGlue());
        return cardChoicePanel;
    }

    /**
     * Initializes the slider adjustment panel
     * @return the slider adjustment panel
     */
    private JPanel initAdjustSliderPanel()
    {
        JPanel adjustSliderPanel = new JPanel();
        adjustSliderPanel.setLayout(new BoxLayout(adjustSliderPanel, BoxLayout.Y_AXIS));
        focusDisplay = new JLabel("", JLabel.LEFT);
        focusDisplay.setAlignmentX(Component.LEFT_ALIGNMENT);
        skillSliderDisplays = new JTextPane[3];
        skillSliders = new JSlider[3];
        adjustSliderPanel.add(focusDisplay);
        for (int i = 0; i < 3; i++)
        {
            skillSliderDisplays[i] = new JTextPane();
            skillSliderDisplays[i].setDocument(
                    new DefaultStyledDocument(infoDisplay.getStyleContext()));
            skillSliderDisplays[i].setAlignmentX(Component.LEFT_ALIGNMENT);
            skillSliderDisplays[i].setOpaque(false);
            skillSliderDisplays[i].setEditable(false);
            skillSliderDisplays[i].setFocusable(false);
            ImageUtils.setFixedSize(skillSliderDisplays[i], 135, 40);
            JPanel sliderPane = new JPanel();
            sliderPane.setLayout(new BoxLayout(sliderPane, BoxLayout.X_AXIS));
            skillSliders[i] = new JSlider(0, 3, 0);
            skillSliders[i].setAlignmentY(Component.TOP_ALIGNMENT);
            skillSliders[i].setMinorTickSpacing(1);
            skillSliders[i].setPaintLabels(false);
            skillSliders[i].setSnapToTicks(true);
            skillSliders[i].addChangeListener(skillSliderListener);
            ImageUtils.setFixedSize(skillSliders[i], 70, 30);
            sliderPane.add(Box.createHorizontalStrut(55));
            sliderPane.add(skillSliders[i]);
            sliderPane.setAlignmentX(Component.LEFT_ALIGNMENT);
            ImageUtils.setFixedSize(sliderPane, 135, 15);
            adjustSliderPanel.add(sliderPane);
            adjustSliderPanel.add(skillSliderDisplays[i]);
        }

        return adjustSliderPanel;
    }

    /**
     * Initializes the roll panel
     * @return the roll panel
     */
    private JPanel initRollPanel()
    {
        // initialize roll panel
        JPanel rollPanel = new JPanel();
        rollPanel.setLayout(new BoxLayout(rollPanel, BoxLayout.Y_AXIS));
        diceDisplayModel = new DefaultListModel();
        diceList = new JList(diceDisplayModel);
        ImageUtils.setFixedSize(diceList, 150, 180);
        diceList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
        diceList.setVisibleRowCount(0);
        diceList.setOpaque(false);
        diceList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        diceList.setCellRenderer(new ListCellRenderer(){
            @Override
            public Component getListCellRendererComponent(JList list,
                    Object value, int index, boolean isSelected, boolean cellHasFocus)
            {
                if (value instanceof Integer)
                {
                    int val = (Integer)value;
                    if (val >= 1 && val <= 6)
                    {
                        ImageLabel ilabel = new ImageLabel(diceIcons[val - 1]);
                        if (isSelected)
                        {
                            ilabel.setBorder(BorderFactory.createLineBorder(Color.blue, 1));
                        }
                        else
                        {
                            ilabel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
                        }
                        return ilabel;
                    }
                }
                AHLogger.error("Invalid values in dice table");
                return new JLabel("Error");
            }
        });

        // initialize roll panel list
        rollPanelListModel = new DefaultComboBoxModel();
        rollPanelList = new JComboBox(rollPanelListModel);
        rollPanelList.setEditable(false);
        ImageUtils.setFixedSize(rollPanelList, ACTION_LIST_WIDTH, ACTION_LIST_HEIGHT);
        rollPanelList.setRenderer(new MultilineListCellRenderer(ACTION_LISTCELL_WIDTH)
        {
            public Component getListCellRendererComponent(JList list,
                    Object value, int index, boolean isSelected, boolean cellHasFocus)
            {
                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (value instanceof ModEffect)
                {
                    ModEffect mod = (ModEffect)value;
                    this.setText(mod.getDisplayString());
                }
                else if (value != null)
                {
                    this.setText(value.toString());
                }
                setToolTipText(this.getText());
                list.setToolTipText(this.getText());
                return this;
            }
        });
        rollPanelList.addActionListener(effectListener);
        // initialize buttons
        rollEffectButton = new JButton("Use effect");
        rollEffectButton.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.black, 1),
                BorderFactory.createEmptyBorder(3, 4, 3, 4)));
        rollEffectButton.setActionCommand("actionUseEffect");
        rollClueButton = new JButton("Use clue");
        rollClueButton.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.black, 1),
                BorderFactory.createEmptyBorder(3, 4, 3, 4)));
        rollClueButton.setActionCommand("actionUseClue");
        JPanel subPanel = new JPanel();
        subPanel.setLayout(new BoxLayout(subPanel, BoxLayout.X_AXIS));
        subPanel.add(rollClueButton);
        subPanel.add(rollEffectButton);

        rollPanel.add(diceList);
        rollPanel.add(Box.createVerticalStrut(2));
        rollPanel.add(rollPanelList);
        rollPanel.add(Box.createVerticalStrut(2));
        rollPanel.add(subPanel);
        return rollPanel;
    }

    /**
     * Initializes the movement panel
     * @return the movement panel
     */
    private JPanel initMovementPanel()
    {
        JPanel movePanel = new JPanel();
        movePanel.setLayout(new BoxLayout(movePanel, BoxLayout.Y_AXIS));
        /* Create Movement Remaining display */
        moveRemainDisplay = new JLabel("Movement Left: ", JLabel.CENTER);
        moveRemainDisplay.setAlignmentX(Component.CENTER_ALIGNMENT);
        moveRemainDisplay.setFont(new Font("Arial", Font.PLAIN, 14));
        movePanelListModel = new DefaultComboBoxModel();
        movePanelList = new JComboBox(movePanelListModel);
        movePanelList.setAlignmentX(Component.CENTER_ALIGNMENT);
        movePanelList.setEditable(false);
        ImageUtils.setFixedSize(movePanelList, ACTION_LIST_WIDTH, ACTION_LIST_HEIGHT);
        movePanelList.addActionListener(effectListener);
        movePanelList.setRenderer(new MultilineListCellRenderer(ACTION_LISTCELL_WIDTH)
        {
            public Component getListCellRendererComponent(JList list,
                    Object value, int index, boolean isSelected, boolean cellHasFocus)
            {
                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (value instanceof UsableEffect)
                {
                    UsableEffect ueff = (UsableEffect)value;
                    if (ueff.getEvent().getDescription() != null)
                    {
                        this.setText(ueff.getSource().getName() + ": " +
                                ueff.getEvent().getDescription());
                    }
                    else
                    {
                        this.setText(ueff.getSource().getName());
                    }
                }
                else
                {
                    if (value != null)
                    {
                        this.setText(value.toString());
                    }
                }
                setToolTipText(this.getText());
                list.setToolTipText(this.getText());
                return this;
            }
        });

        tradeRecipientList = new JComboBox(actionListModel);
        tradeRecipientList.setAlignmentX(Component.CENTER_ALIGNMENT);
        tradeRecipientList.setEditable(false);
        ImageUtils.setFixedSize(tradeRecipientList, ACTION_LIST_WIDTH, ACTION_LIST_HEIGHT);
        tradeRecipientList.setRenderer(new MultilineListCellRenderer(ACTION_LISTCELL_WIDTH)
        {
            public Component getListCellRendererComponent(JList list,
                    Object value, int index, boolean isSelected, boolean cellHasFocus)
            {
                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (value instanceof NamedEntity)
                {
                    NamedEntity ne = (NamedEntity)value;
                    this.setText(ne.getName());
                }
                else if (value instanceof String)
                {
                    this.setText(value.toString());
                }

                setToolTipText(this.getText());
                list.setToolTipText(this.getText());
                return this;
            }
        });

        // initialize buttons
        moveEffectButton = new JButton("Use effect");
        moveEffectButton.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.black, 1),
                BorderFactory.createEmptyBorder(3, 4, 3, 4)));
        moveEffectButton.setActionCommand("actionUseEffect");
        moveEffectButton.setAlignmentX(Component.CENTER_ALIGNMENT);
        moveTradeButton = new JButton("Trade");
        moveTradeButton.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.black, 1),
                BorderFactory.createEmptyBorder(3, 4, 3, 4)));
        moveTradeButton.setActionCommand("actionTrade");
        moveTradeButton.setAlignmentX(Component.CENTER_ALIGNMENT);
        movePanel.add(moveRemainDisplay);
        movePanel.add(Box.createVerticalStrut(10));
        movePanel.add(movePanelList);
        movePanel.add(Box.createVerticalStrut(2));
        movePanel.add(moveEffectButton);
        movePanel.add(Box.createVerticalStrut(15));
        movePanel.add(tradeRecipientList);
        movePanel.add(Box.createVerticalStrut(2));
        movePanel.add(moveTradeButton);
        movePanel.add(Box.createVerticalGlue());
        return movePanel;
    }

    /**
     * Initializes the trade panel in the action pane
     * @return the trade panel
     */
    @SuppressWarnings("serial")
    private JPanel initTradePanel()
    {
        JPanel tradePanel = new JPanel();
        tradePanel.setLayout(new BoxLayout(tradePanel, BoxLayout.Y_AXIS));
        CheckList tradeList1 = new CheckList();
        tradeList1.setFixedCellWidth(135);
        tradeListModel1 = new DefaultListCheckModel();
        tradeList1.setModel(tradeListModel1);
        CheckList tradeList2 = new CheckList();
        tradeListModel2 = new DefaultListCheckModel();
        tradeList2.setFixedCellWidth(135);
        tradeList2.setModel(tradeListModel2);

        CheckListRenderer lcr = new CheckListRenderer()
        {
            public String getText(Object value)
            {
                if (value instanceof InvestigatorCard)
                {
                    InvestigatorCard invcard = (InvestigatorCard)value;
                    setToolTipText(invcard.getName());
                    setBackground(invcard.getPreferredColor());
                    return invcard.getName();
                }
                setBackground(Color.white);
                return value.toString();
            }
        };
        tradeList1.setCellRenderer(lcr);
        tradeList2.setCellRenderer(lcr);
        tradeList1.setName("Trade List 1");
        tradeList2.setName("Trade List 2");
        tradeList1.addMouseListener(listClickListener);
        tradeList2.addMouseListener(listClickListener);
        JScrollPane jsp1 = new JScrollPane(tradeList1);
        JScrollPane jsp2 = new JScrollPane(tradeList2);
        jsp1.setPreferredSize(new Dimension(155, 100));
        jsp1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        jsp2.setPreferredSize(new Dimension(155, 100));
        jsp2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        JLabel text1 = new JLabel("Your items", JLabel.CENTER);
        text1.setAlignmentX(Component.CENTER_ALIGNMENT);
        JLabel text2 = new JLabel("Their items", JLabel.CENTER);
        text2.setAlignmentX(Component.CENTER_ALIGNMENT);
        tradePanel.add(Box.createVerticalStrut(5));
        tradePanel.add(text1);
        tradePanel.add(jsp1);
        tradePanel.add(Box.createVerticalStrut(5));
        tradePanel.add(text2);
        tradePanel.add(jsp2);
        return tradePanel;
    }

    /**
     * Initializes the trade acceptance panel in the action pane
     * @return the trade acceptance panel
     */
    @SuppressWarnings("serial")
    private JPanel initTradeAcceptPanel()
    {
        JPanel acceptPanel = new JPanel();
        acceptPanel.setLayout(new BoxLayout(acceptPanel, BoxLayout.Y_AXIS));
        acceptListModel1 = new DefaultListCheckModel();
        CheckList acceptList1 = new CheckList();
        acceptList1.setFixedCellWidth(135);
        acceptList1.setModel(acceptListModel1);
        acceptListModel2 = new DefaultListCheckModel();
        CheckList acceptList2 = new CheckList();
        acceptList2.setFixedCellWidth(135);
        acceptList2.setModel(acceptListModel2);
        CheckListRenderer lcr = new CheckListRenderer()
        {
            public String getText(Object value)
            {
                if (value instanceof InvestigatorCard)
                {
                    InvestigatorCard invcard = (InvestigatorCard)value;
                    setToolTipText(invcard.getName());
                    setBackground(invcard.getPreferredColor());
                    return invcard.getName();
                }
                setBackground(Color.white);
                return value.toString();
            }

            public Component getListCellRendererComponent(JList list, Object value, int index,
                    boolean isSelected, boolean cellHasFocus)
            {
                Component comp = super.getListCellRendererComponent(list, value,
                        index, isSelected, cellHasFocus);

                comp.setEnabled(true);
                comp.setFocusable(false);
                return comp;
            }
        };
        acceptList1.setCellRenderer(lcr);
        acceptList2.setCellRenderer(lcr);
        acceptList1.setName("Accept List 1");
        acceptList2.setName("Accept List 2");
        acceptList1.addMouseListener(listClickListener);
        acceptList2.addMouseListener(listClickListener);
        JScrollPane jsp1 = new JScrollPane(acceptList1);
        JScrollPane jsp2 = new JScrollPane(acceptList2);
        jsp1.setPreferredSize(new Dimension(155, 100));
        jsp1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        jsp2.setPreferredSize(new Dimension(155, 100));
        jsp2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        JLabel text1 = new JLabel("Your items", JLabel.CENTER);
        text1.setAlignmentX(Component.CENTER_ALIGNMENT);
        JLabel text2 = new JLabel("Their items", JLabel.CENTER);
        text2.setAlignmentX(Component.CENTER_ALIGNMENT);
        acceptPanel.add(Box.createVerticalStrut(5));
        acceptPanel.add(text1);
        acceptPanel.add(jsp1);
        acceptPanel.add(Box.createVerticalStrut(5));
        acceptPanel.add(text2);
        acceptPanel.add(jsp2);

        return acceptPanel;
    }

    /**
     * Initializes the combat panel in the action pane
     * @return the combat panel
     */
    @SuppressWarnings("serial")
    private JPanel initCombatPanel()
    {
        JPanel combatPanel = new JPanel();
        combatPanel.setLayout(new BoxLayout(combatPanel, BoxLayout.Y_AXIS));
        /* The top of the panel contains the monster name and equipped weapons */
        monsterNameDisplay = new JLabel("", JLabel.CENTER);
        monsterNameDisplay.setAlignmentX(Component.CENTER_ALIGNMENT);
        monsterNameDisplay.setFont(arkhamFont.deriveFont(Font.PLAIN, 16));
        monsterNameDisplay.addMouseListener(new MouseAdapter(){
           public void mousePressed(MouseEvent e)
           {
               if (combatPanelMon != null)
               {
                   infoDisplay.displayMonster(combatPanelMon);
               }
               e.consume();
           }
        });
        UnderlinedLabel elabel = new UnderlinedLabel("Equipped", JLabel.LEFT);
        elabel.setAlignmentX(Component.CENTER_ALIGNMENT);
        handsFreeDisplay = new JLabel("Hands Left: 2");
        handsFreeDisplay.setAlignmentX(Component.CENTER_ALIGNMENT);

        equippedListModel = new DefaultListModel();
        equippedList = new JList(equippedListModel);
        equippedList.setAlignmentX(Component.CENTER_ALIGNMENT);
        equippedList.setName("Equipped List");
        equippedList.addMouseListener(listClickListener);
        equippedList.setFocusable(false);
        equippedList.setFixedCellWidth(145);
        equippedList.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createBevelBorder(BevelBorder.RAISED),
                BorderFactory.createEmptyBorder(0, 2, 0, 2)));
        equippedList.setCellRenderer(new DefaultListCellRenderer()
        {
            public Component getListCellRendererComponent(JList list,
                    Object value, int index, boolean isSelected, boolean cellHasFocus)
            {
                super.getListCellRendererComponent(list, value, index, false, false);
                if (value instanceof InvestigatorCard)
                {
                    InvestigatorCard invcard = (InvestigatorCard)value;
                    setToolTipText(invcard.getName());
                    setText(invcard.getName());
                    setBackground(invcard.getPreferredColor());
                }
                else if (value instanceof PersonalEffect)
                {
                    PersonalEffect peff = (PersonalEffect)value;
                    setToolTipText(peff.getName());
                    setText(peff.getName());
                    setBackground(peff.getPreferredColor());
                }
                else if (value instanceof String)
                {
                    setText(value.toString());
                    setBackground(Color.white);
                }
                return this;
            }
        });

        UnderlinedLabel alabel = new UnderlinedLabel("Available", JLabel.CENTER);
        alabel.setAlignmentX(Component.CENTER_ALIGNMENT);
        weaponsList = new CheckList();
        weaponsList.setFixedCellWidth(135);
        weaponsListModel = new DefaultListCheckModel();
        weaponsListModel.addListCheckListener(new ListCheckListener()
        {

            @Override
            public void addCheck(ListEvent e)
            {
                if (e.getValues().size() == 1)
                {
                    Object obj = e.getValues().get(0);
                    if (obj instanceof InvestigatorCard)
                    {
                        InvestigatorCard card = (InvestigatorCard)obj;
                        int hands = XmlTools.parseInt(card.getProperty("Hands"));
                        handsFree -= hands;
                        if (handsFree >= 0)
                        {
                            handsFreeDisplay.setText("Hands Free: " + handsFree);
                        }
                        else
                        {
                            handsFreeDisplay.setText("Not enough hands!");
                            actionOkButton.setEnabled(false);
                        }
                    }
                }
            }

            @Override
            public void removeCheck(ListEvent e)
            {
                if (e.getValues().size() == 1)
                {
                    Object obj = e.getValues().get(0);
                    if (obj instanceof InvestigatorCard)
                    {
                        InvestigatorCard card = (InvestigatorCard)obj;
                        int hands = XmlTools.parseInt(card.getProperty("Hands"));
                        handsFree += hands;
                        if (handsFree >= 0)
                        {
                            handsFreeDisplay.setText("Hands Free: " + handsFree);
                            actionOkButton.setEnabled(true);
                        }
                        else
                        {
                            handsFreeDisplay.setText("Not enough hands!");
                            actionOkButton.setEnabled(false);
                        }
                    }
                }
            }

        });
        weaponsList.setModel(weaponsListModel);
        weaponsList.setName("Weapons List");
        weaponsList.addMouseListener(listClickListener);
        weaponsList.setCellRenderer(new CheckListRenderer()
        {
            public String getText(Object value)
            {
                if (value instanceof InvestigatorCard)
                {
                    InvestigatorCard invcard = (InvestigatorCard)value;
                    setToolTipText(invcard.getName());
                    setBackground(invcard.getPreferredColor());
                    return invcard.getName();
                }
                else if (value instanceof PersonalEffect)
                {
                    PersonalEffect peff = (PersonalEffect)value;
                    setToolTipText(peff.getName());
                    setBackground(peff.getPreferredColor());
                    return peff.getName();
                }
                setBackground(Color.white);
                return value.toString();
            }
        });
        JScrollPane jsp1 = new JScrollPane(weaponsList);
        jsp1.setPreferredSize(new Dimension(155, 60));
        jsp1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);

        combatPanelListModel = new DefaultComboBoxModel();
        combatPanelList = new JComboBox(combatPanelListModel);
        combatPanelList.setAlignmentX(Component.CENTER_ALIGNMENT);
        combatPanelList.setEditable(false);
        ImageUtils.setFixedSize(combatPanelList, ACTION_LIST_WIDTH, ACTION_LIST_HEIGHT);
        combatPanelList.addActionListener(effectListener);
        combatPanelList.setRenderer(new MultilineListCellRenderer(ACTION_LISTCELL_WIDTH)
        {
            public Component getListCellRendererComponent(JList list,
                    Object value, int index, boolean isSelected, boolean cellHasFocus)
            {
                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (value instanceof ModEffect)
                {
                    ModEffect mod = (ModEffect)value;
                    this.setText(mod.getDisplayString());
                }
                else
                {
                    if (value != null)
                    {
                        this.setText(value.toString());
                    }
                }
                setToolTipText(this.getText());
                list.setToolTipText(this.getText());
                return this;
            }
        });
        /* Initialize Buttons */
        combatEvadeButton = new JButton("Evade");
        combatEvadeButton.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.black, 1),
                BorderFactory.createEmptyBorder(3, 4, 3, 4)));
        combatEvadeButton.setActionCommand("actionEvade");
        combatEffectButton = new JButton("Use");
        combatEffectButton.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.black, 1),
                BorderFactory.createEmptyBorder(3, 4, 3, 4)));
        combatEffectButton.setActionCommand("actionUseEffect");
        JPanel subPanel2 = new JPanel();
        subPanel2.setLayout(new BoxLayout(subPanel2, BoxLayout.X_AXIS));
        subPanel2.add(combatEvadeButton);
        subPanel2.add(Box.createHorizontalStrut(5));
        subPanel2.add(combatEffectButton);
        subPanel2.setAlignmentX(Component.CENTER_ALIGNMENT);

        combatPanel.add(monsterNameDisplay);
        combatPanel.add(Box.createVerticalStrut(3));
        combatPanel.add(elabel);
        combatPanel.add(Box.createVerticalStrut(1));
        combatPanel.add(equippedList);
        combatPanel.add(Box.createVerticalStrut(3));
        combatPanel.add(handsFreeDisplay);
        combatPanel.add(alabel);
        combatPanel.add(Box.createVerticalStrut(1));
        combatPanel.add(jsp1);
        combatPanel.add(Box.createVerticalStrut(8));
        combatPanel.add(combatPanelList);
        combatPanel.add(subPanel2);
        combatPanel.add(Box.createVerticalGlue());
        return combatPanel;
    }

    private ListCellRenderer invCardCellRenderer = new DefaultListCellRenderer()
    {
        public Component getListCellRendererComponent(JList list,
                Object value, int index, boolean isSelected, boolean cellHasFocus)
        {
            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            Font listFont = new Font("Times New Roman", Font.PLAIN, 12);
            setFont(listFont);
            setBorder(BorderFactory.createLineBorder(Color.black));
            if (value instanceof InvestigatorCard)
            {
                InvestigatorCard invcard = (InvestigatorCard)value;
                setToolTipText(invcard.getName());
                int width = getFontMetrics(listFont).stringWidth(invcard.getName());
                if (width > 135)
                {
                    StringBuffer sb = new StringBuffer(invcard.getName());
                    while (width > 120 && sb.length() > 10)
                    {
                        sb.deleteCharAt(sb.length() - 1);
                        width = getFontMetrics(listFont).stringWidth(sb.toString());
                    }
                    setText(sb.toString() + "...");
                }
                else
                {
                    setText(invcard.getName());
                }
                setBackground(invcard.getPreferredColor());
            }
            else if (value instanceof String)
            {
                setText(value.toString());
                setBackground(Color.white);
            }
            return this;
        }
    };

    private ChangeListener skillSliderListener = new ChangeListener()
    {
        @Override
        public void stateChanged(ChangeEvent e)
        {
            JSlider js = (JSlider)e.getSource();
            if (js.getValueIsAdjusting() || actionPanelInv == null)
            {
                return;
            }
            int sum = 0;
            for (int i = 0; i < actionPanelInv.getSkillSliders().length; i++)
            {
                sum += Math.abs(actionPanelInv.getSkillSlider(i) - skillSliders[i].getValue());
            }
            int remFocus = actionPanelInv.getFocus() - sum;
            if (remFocus < 0)
            {
                focusDisplay.setText("Not enough focus!");
                actionOkButton.setEnabled(false);
            }
            else
            {
                if (remFocus > 999)
                {
                    focusDisplay.setText("Focus: *");
                }
                else
                {
                    focusDisplay.setText("Focus: " + remFocus);
                }
                actionOkButton.setEnabled(true);
            }
            actionPanel.repaint();
        }
    };

    private ActionListener investigatorSelectionListener = new ActionListener()
    {
        public void actionPerformed(ActionEvent e)
        {
            Object obj = actionListModel.getSelectedItem();
            if (obj instanceof Investigator)
            {
                Investigator inv = (Investigator)obj;
                infoDisplay.displayInvestigator(inv);
            }
        }
    };

    private ActionListener monsterListener = new ActionListener()
    {
        public void actionPerformed(ActionEvent e)
        {
            Object obj = actionListModel.getSelectedItem();
            if (obj instanceof Monster)
            {
                Monster mon = (Monster)obj;
                infoDisplay.displayMonster(mon);
            }
        }
    };

    private ActionListener effectListener = new ActionListener()
    {
        public void actionPerformed(ActionEvent e)
        {
            if (!(e.getSource() instanceof JComboBox))
            {
                return;
            }
            JComboBox combobox = (JComboBox)e.getSource();
            Object obj = combobox.getModel().getSelectedItem();
            if (obj instanceof ModEffect)
            {
                ModEffect mod = (ModEffect)obj;
                if (mod.getSource() instanceof InvestigatorCard)
                {
                    infoDisplay.displayInvestigatorCard((InvestigatorCard)mod.getSource());
                }
                else if (mod.getSource() instanceof InvestigatorPower)
                {
                    infoDisplay.displayInvestigatorPower((InvestigatorPower)mod.getSource());
                }
                else if (mod.getSource() instanceof PersonalEffect)
                {
                    infoDisplay.displayPersonalEffect((PersonalEffect)mod.getSource());
                }
            }
            else if (obj instanceof UsableEffect)
            {
                UsableEffect ueff = (UsableEffect)obj;
                if (ueff.getSource() instanceof InvestigatorCard)
                {
                    infoDisplay.displayInvestigatorCard((InvestigatorCard)ueff.getSource());
                }
                else if (ueff.getSource() instanceof InvestigatorPower)
                {
                    infoDisplay.displayInvestigatorPower((InvestigatorPower)ueff.getSource());
                }
                else if (ueff.getSource() instanceof PersonalEffect)
                {
                    infoDisplay.displayPersonalEffect((PersonalEffect)ueff.getSource());
                }
            }
        }
    };

    private ActionListener encounterListener = new ActionListener()
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            if (!(e.getSource() instanceof JComboBox))
            {
                return;
            }
            JComboBox combobox = (JComboBox)e.getSource();
            int index = combobox.getSelectedIndex();
            EventNode en = encounterList.get(index);
            if (en == null)
            {
                return;
            }
            infoDisplay.displayEncounter(en.getLocation(), en.getDescription());
        }
    };

    private ActionListener gateCardListener = new ActionListener()
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            if (!(e.getSource() instanceof JComboBox))
            {
                return;
            }
            JComboBox combobox = (JComboBox)e.getSource();
            Object obj = combobox.getSelectedItem();
            if (obj instanceof GateCard)
            {
                GateCard card = (GateCard)obj;
                infoDisplay.displayGateCard(card, owLocation);
            }
        }
    };

    /**
     * Gets the info display
     * @return the info display
     */
    public ArkhamInfoPane getInfoDisplay() {
        return infoDisplay;
    }

    /**
     * Gets the info panel
     * @return the info panel
     */
    public JPanel getInfoPanel() {
        return infoPanel;
    }

    /**
     * Gets the main panel
     * @return the mainPanel
     */
    public JPanel getMainPanel() {
        return mainPanel;
    }

    /**
     * Gets the phase panel
     * @return the phase panel
     */
    public JPanel getPhasePanel() {
        return phasePanel;
    }

    /**
     * Gets the investigator info panel
     * @return the investigator info panel
     */
    public JTabbedPane getInvestigatorPanel() {
        return investigatorPanel;
    }

    /**
     * Gets the investigator being displayed;
     * @return the displayedInvestigator
     */
    public Investigator getDisplayedInvestigator() {
        return displayedInvestigator;
    }

    /**
     * Gets the global panel
     *
     * @return the globalPanel
     */
    public JPanel getGlobalPanel() {
        return globalPanel;
    }

    public BoardManager getBoardManager()
    {
        // TODO: remove when not testing
        return boardManager;
    }

    /**
     * Gets the map panel
     * @return the map panel
     */
    public ResizableImagePanel getMapPanel()
    {
        return mapPanel;
    }

    /**
     * Get the player panel
     * @param index the index of the player
     * @return the player panel, or null if not found
     */
    public JPanel getPlayerPanel(int index)
    {
        if (index < 0 || index >= gameData.getNumPlayers())
        {
            return null;
        }
        return playerPanel[index];
    }

    public ResizableImagePanel getMouseOverPanel()
    {
        // TODO: remove when not testing
        return mouseOverPanel;
    }

    public JLayeredPane getLayeredPane()
    {
        return layeredPane;
    }

    public JPanel getActionPanel()
    {
        return actionPanel;
    }

    public SlidingPanel getSlidingPanel()
    {
        return slidingPanel;
    }

    /**
     * Gets an array of all action buttons registered to the action panel
     * @return an array of action buttons
     */
    public JButton[] getActionButtons()
    {
        return new JButton[] {actionOkButton, actionCancelButton, rollClueButton,
                rollEffectButton, moveEffectButton, moveTradeButton, combatEffectButton,
                combatEvadeButton};
    }

    /**
     * Gets the action list
     * @return the action list
     */
    public JComboBox getActionList()
    {
        return actionList;
    }

    /**
     * Gets the roll panel list
     * @return the roll panel list
     */
    public JComboBox getRollPanelList()
    {
        return rollPanelList;
    }

    /**
     * Gets the move panel list
     * @return the move panel list
     */
    public JComboBox getMovePanelList()
    {
        return movePanelList;
    }

    /**
     * Gets the combat panel list
     * @return the combat panel list
     */
    public JComboBox getCombatPanelList()
    {
        return combatPanelList;
    }

    /**
     * Gets the dice list on the Roll Panel
     * @return the dice list
     */
    public JList getDiceList()
    {
        return diceList;
    }

    /**
     * @return the cardListChoices
     */
    public int getCardListChoices() {
        return cardListChoices;
    }

    public DefaultListModel getCardChoiceListModel()
    {
        return cardChoiceListModel;
    }

    /**
     * @return the investigator card list model
     */
    public DefaultListModel getInvCardListModel()
    {
        return invCardListModel;
    }

    /**
     * Gets the skill slider position at a certain index
     * @param index the index of the skill slider
     * @return the skill slider
     */
    public int getSkillSliderPosition(int index)
    {
        if (index < 0 || index >= skillSliders.length)
        {
            return 0;
        }
        return skillSliders[index].getValue();
    }

    /**
     * Gets the list of checked items in the first trade list
     * @return the list of checked items
     */
    public List<Object> getCheckedItems1()
    {
        return tradeListModel1.getCheckeds();
    }

    /**
     * Gets the list of checked items in the second trade list
     * @return the list of checked items
     */
    public List<Object> getCheckedItems2()
    {
        return tradeListModel2.getCheckeds();
    }

    /**
     * Gets the list of checked items in the weapons list
     * @return the list of checked items
     */
    public List<Object> getCheckedWeapons()
    {
        return weaponsListModel.getCheckeds();
    }

    public void updateDisplay()
    {
        updateMainPanel();
        updateGlobalPanel();
        updatePhasePanel();
        if (displayedInvestigator != null)
        {
            updateInvestigatorDisplay(displayedInvestigator);
        }
    }

    /**
     * Updates the main panel view to match the data
     */
    private void updateMainPanel()
    {
        for (int i = 0; i < gameData.getNumPlayers(); i++)
        {
            updatePlayerPanel(i);
        }
        // update map
        boardManager.updateMap();
        mainPanel.repaint();
    }

    /**
     * Updates the player panel to match the investigator data
     * @param index the index to update
     */
    private void updatePlayerPanel(int index)
    {
        Investigator inv = gameData.getInvestigator(index);
        if (inv != null)
        {
            invNameDisplay[index].setText(inv.getName());
            float optimalSize = ImageUtils.calculateFontSize(
                    invNameDisplay[index], invNameDisplay[index].getFont(),
                    95, inv.getName());
            invNameDisplay[index].setFont(arkhamFont.deriveFont(Font.PLAIN, optimalSize));
            if (gameData.getStartingPlayer() == index)
            {
                invNameDisplay[index].setForeground(Color.blue);
            }
            else
            {
                invNameDisplay[index].setForeground(Color.black);
            }
            ownerDisplay[index].setText(gameData.getPlayerName(index));
            stamDisplay[index].setText(String.valueOf(inv.getCurrentStamina()));
            stamDisplay[index].setToolTipText("Stamina: " + inv.getCurrentStamina() +
                    "/" + inv.getMaxStamina());
            sanDisplay[index].setText(String.valueOf(inv.getCurrentSanity()));
            sanDisplay[index].setToolTipText("Sanity: " + inv.getCurrentSanity() +
                    "/" + inv.getMaxSanity());
            clueDisplay[index].setText(String.valueOf(inv.getClues()));
            moneyDisplay[index].setText(String.valueOf(inv.getMoney()));
            updateSkillDisplay(skillDisplay[index], inv);
            pEffectPanel[index].removeAll();
            for (int i = 0; i < inv.getStatusEffects().size(); i++)
            {
                PersonalEffect peff = inv.getStatusEffects().get(i);
                if (peff.getImage() != null)
                {
                    ImageLabel iLabel = new ImageLabel(peff.getImage());
                    pEffectPanel[index].add(iLabel);
                    iLabel.setToolTipText(peff.toString());
                }
            }
            pEffectPanel[index].add(Box.createHorizontalGlue());
            playerPanel[index].repaint();
        }
        else
        {
            // for testing only
            invNameDisplay[index].setText("Not Selected");
            float optimalSize = ImageUtils.calculateFontSize(
                    invNameDisplay[index], invNameDisplay[index].getFont(),
                    95, invNameDisplay[index].getText());
            invNameDisplay[index].setFont(arkhamFont.deriveFont(Font.PLAIN, optimalSize));
            ownerDisplay[index].setText(gameData.getPlayerName(index));
            stamDisplay[index].setText("6");
            sanDisplay[index].setText("4");
            clueDisplay[index].setText("1");
            moneyDisplay[index].setText("12");
            updateSkillDisplay(skillDisplay[index], gameData.getInvestigatorPool().get(2));
            pEffectPanel[index].removeAll();
            ImageLabel iLabel = new ImageLabel(gameData.getPersonalEffect("Blessing").getImage());
            if (index % 2 == 0) pEffectPanel[index].add(iLabel);
            iLabel.setToolTipText("Blessing");
            ImageLabel iLabel2 = new ImageLabel(gameData.getPersonalEffect("Curse").getImage());
            if (index % 2 == 1) pEffectPanel[index].add(iLabel2);
            iLabel2.setToolTipText("Curse");
            pEffectPanel[index].add(Box.createHorizontalGlue());
            playerPanel[index].repaint();
        }
    }

    /**
     * Updates the skill display with fancy colors
     * @param jtp the skill display to update
     * @param inv the investigator to display stats for
     */
    private void updateSkillDisplay(JTextPane jtp, Investigator inv)
    {
        try
        {
            StyledDocument styledDoc = jtp.getStyledDocument();
            styledDoc.remove(0, styledDoc.getLength());
            for (int i = 0; i < 6; i++)
            {
                if (i % 2 == 0)
                {
                    styledDoc.insertString(styledDoc.getLength(),
                            InvestigatorSkill.getSkillName(i).substring(0, 2) + ": ",
                            styledDoc.getStyle("blueblack"));
                    styledDoc.insertString(styledDoc.getLength(),
                            inv.getCurrentSkill(i) + "\n",
                            styledDoc.getStyle("blue"));
                }
                else
                {
                    styledDoc.insertString(styledDoc.getLength(),
                            InvestigatorSkill.getSkillName(i).substring(0, 2) + ": ",
                            styledDoc.getStyle("redblack"));
                    styledDoc.insertString(styledDoc.getLength(),
                            inv.getCurrentSkill(i) + "\n",
                            styledDoc.getStyle("red"));
                }
            }
        }
        catch (BadLocationException e)
        {
            AHLogger.info("ViewManager.updateSkillDisplay", e.toString());
        }
    }

    /**
     * Updates the view to match the underlying data
     */
    private void updateGlobalPanel()
    {
        AncientOne ao = gameData.getAncientOne();
        if (ao != null) {
            aoName.setText(ao.getName());
            doomTrack.setText(ao.getDoomTokens() + " / " + ao.getDoomTrack());
        } else {
            // before game start
            aoName.setText(" - ??? - ");
            doomTrack.setText("? / ?");
            doomTrack.repaint();
        }
        if (gameData.isStarted()) {
            terrorTrack.setText(gameData.getTerrorLevel() + " / 10");
            currentEvent.setText(XmlTools.truncate(gameData.getLastDrawnMythos().getName(), 25, 3));
            currentEvent.setToolTipText(gameData.getLastDrawnMythos().getName());
            // TODO: represent ongoing effects
            globalEffectPanel.removeAll();
            globalEffectPanel.add(Box.createHorizontalGlue());
            for (Environment env : gameData.getActiveEnvironments()) {
                ImageLabel label = new ImageLabel(weatherIcon);
                label.setToolTipText(env.getName());
                globalEffectPanel.add(label);
            }
            if (gameData.getActiveRumor() != null) {
                JLabel label = new ImageLabel(terrorIcon);
                label.setToolTipText(gameData.getActiveRumor().getName());
                globalEffectPanel.add(label);
            }
            globalEffectPanel.add(Box.createHorizontalStrut(5));
        } else {
            terrorTrack.setText(gameData.getTerrorLevel() + " / 10");
            currentEvent.setText("Blackest Night");
            globalEffectPanel.removeAll();
            globalEffectPanel.add(Box.createHorizontalGlue());
            JLabel label = new ImageLabel(weatherIcon);
            label.setToolTipText("Blackest Night");
            globalEffectPanel.add(label);
            globalEffectPanel.add(Box.createHorizontalStrut(5));
        }
        globalPanel.repaint();
    }

    public void updatePhasePanel()
    {
        String phaseText = "";
        int phaseIndex = 0;
        if (!gameData.isStarted())
        {
            phaseIndex = 0;
            switch (gameData.getGameSetupState())
            {
            case PRE_SETUP:
            case SELECT_INVESTIGATORS:
            case SELECT_ANCIENT_ONE:
                phaseText = "Setup";
                break;
            case RECEIVE_STARTING_ITEMS:
                phaseText = "Items";
                break;
            case FIRST_MYTHOS:
                phaseText = "Mythos";
                break;
            }
        }
        else
        {
            switch (gameData.getGamePhase())
            {
            case PRE_UPKEEP:
            case UPKEEP:
                phaseText = "Upkeep";
                phaseIndex = 1;
                break;
            case MOVEMENT:
                phaseText = "Movement";
                phaseIndex = 2;
                break;
            case ARKHAM_ENCOUNTER:
                phaseText = "Arkham";
                phaseIndex = 3;
                break;
            case OTHER_WORLD_ENCOUNTER:
                phaseText = "Other\nWorld";
                phaseIndex = 4;
                break;
            case MYTHOS_GATE:
            case MYTHOS_CLUE:
            case MYTHOS_MOVEMENT:
            case MYTHOS_SPECIAL:
            case MYTHOS_EFFECT:
                phaseText = "Mythos";
                phaseIndex = 5;
                break;
            }
        }
        String protoPhrase = phaseText;
        if (phaseText.indexOf("\n") != -1)
        {
            protoPhrase = phaseText.substring(0, phaseText.indexOf("\n"));
        }
        float fontSize = ImageUtils.calculateFontSize(phaseDisplay, kingFont, 60, protoPhrase);
        phaseDisplay.setFont(kingFont.deriveFont(Font.PLAIN, fontSize));
        phaseDisplay.setText(phaseText);
        phaseImage.setImage(phaseProgress[phaseIndex]);
        phasePanel.repaint();
    }

    /**
     * Updates the background and the border of the player panels to reflect
     * the active and current player. The active player will have the
     * background highlighted, while the current player will have a red
     * border.
     * @param currentPlayer the player whose turn it is to act
     * @param activePlayer the player who the owner is currently controlling
     */
    public void updateTurnOrder(int currentPlayer, int activePlayer)
    {
        for (int i = 0; i < playerPanel.length; i++)
        {
            if (i == activePlayer)
            {
                playerPanel[i].setBackground(new Color(255, 255, 192));
            }
            else
            {
                playerPanel[i].setBackground(new Color(235, 235, 235));
            }

            if (i == currentPlayer)
            {
                playerPanel[i].setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED,
                        new Color(255, 0, 0), new Color(128, 0, 0)));
            }
            else
            {
                playerPanel[i].setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
            }
        }
        mainPanel.repaint();
    }

    /**
     * Updates the investigator display to display the details of the
     * given investigator
     * @param inv the investigator to display
     */
    public void updateInvestigatorDisplay(Investigator inv)
    {
        // update personal pane
        displayedInvestigator = inv;
        persPaneName.setText(inv.getName());
        float optimalSize = ImageUtils.calculateFontSize(
                persPaneName, arkhamFont, personalPane.getWidth(), inv.getName());
        optimalSize = Math.min(20, optimalSize);
        persPaneName.setFont(arkhamFont.deriveFont(Font.PLAIN, optimalSize));
        invenPaneName.setText(inv.getName());
        invenPaneName.setFont(arkhamFont.deriveFont(Font.PLAIN, optimalSize));
        effectsPaneName.setText(inv.getName());
        effectsPaneName.setFont(arkhamFont.deriveFont(Font.PLAIN, optimalSize));
        persPaneProfession.setText("the " + inv.getProfession());
        optimalSize = ImageUtils.calculateFontSize(
                persPaneProfession, arkhamFont, personalPane.getWidth(), "the " + inv.getProfession());
        persPaneProfession.setFont(arkhamFont.deriveFont(Font.PLAIN, Math.min(17, optimalSize)));
        /* Display a status string if investigator is affected by it */
        String statusString = "";
        if (inv.isDelayed())
        {
            statusString += "Delayed";
        }
        if (statusString.equals(""))
        {
            persPaneStatus.setVisible(false);
        }
        else
        {
            persPaneStatus.setText(statusString);
            persPaneStatus.setVisible(true);
            optimalSize = ImageUtils.calculateFontSize(
                    persPaneStatus, arkhamFont, personalPane.getWidth(), statusString);
            persPaneStatus.setFont(arkhamFont.deriveFont(Font.PLAIN, Math.min(16, optimalSize)));
        }
        powerListModel.clear();
        for (InvestigatorPower invpower : inv.getSpecialPowers())
        {
            powerListModel.addElement(invpower);
        }
        // update inventory pane
        inventoryListModel.clear();
        inv.sortInvcards();
        if (inv.getInvcards().size() > 0)
        {
            for (InvestigatorCard invcard : inv.getInvcards())
            {
                inventoryListModel.addElement(invcard);
            }
        }
        else
        {
            inventoryListModel.addElement("<None>");
        }
        effectListModel.clear();
        if (inv.getStatusEffects().size() > 0)
        {
            for (PersonalEffect peff : inv.getStatusEffects())
            {
                effectListModel.addElement(peff);
            }
        }
        else
        {
            effectListModel.addElement("<None>");
        }
        monsterTrophyListModel.clear();
        if (inv.getMonsterTrophies().size() > 0)
        {
            for (Monster mon : inv.getMonsterTrophies())
            {
                monsterTrophyListModel.addElement(mon);
            }
        }
        else
        {
            monsterTrophyListModel.addElement("<None>");
        }
        gateTrophyListModel.clear();
        if (inv.getGateTrophies().size() > 0)
        {
            for (GateTile gate : inv.getGateTrophies())
            {
                gateTrophyListModel.addElement(gate);
            }
        }
        else
        {
            gateTrophyListModel.addElement("<None>");
        }
        investigatorPanel.repaint();
    }

    /**
     * Updates the title and description and current turn on the action
     * panel without changing anything else.
     * @param title the title
     * @param description the description
     */
    public void updateActionPanelText(String title, String description)
    {
        actionTitle.setText(title);
        helpString.setText(description);
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        actionPanel.repaint();
    }

    /**
     * Updates the action panel for investigator selection
     * @param invlist the list of choices to choose from
     * @param active whether the current player is the active player
     */
    public void updateForInvSelection(List<Investigator> invlist, boolean active)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText("Select Investigators");
        helpString.setText("Select your investigator from the drop down box below");
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Action List");
        actionListModel.removeAllElements();
        for (Investigator inv : invlist)
        {
            actionListModel.addElement(inv);
        }
        // clear action listeners
        ActionListener[] actionListArr = actionList.getActionListeners();
        for (int i = 0; i < actionListArr.length; i++)
        {
            actionList.removeActionListener(actionListArr[i]);
        }
        // add investigator selection listener
        actionList.addActionListener(investigatorSelectionListener);
        actionList.setEnabled(true);
        infoDisplay.displayInvestigator(invlist.get(0));
        actionOkButton.setEnabled(active);
        slidingPanel.setExtended(true);
        actionCancelButton.setVisible(false);
    }

    /**
     * Updates the action panel for a situation with no action. The
     * OK button is disabled and only a message is displayed
     * @param title the title to show
     * @param description the description to show
     */
    public void updateForNoAction(String title, String description)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Empty Panel");
        actionOkButton.setEnabled(false);
        actionCancelButton.setVisible(false);
    }

    /**
     * Updates the action panel for a situation where the OK button can
     * be pressed, but no other action can be done.
     * @param title the title to show
     * @param description the description to show
     */
    public void updateForConfirmAction(String title, String description)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Empty Panel");
        actionOkButton.setEnabled(true);
        actionCancelButton.setVisible(false);
    }

    /**
     * Updates the action panel to display a list of cards which can be selected
     * from. User may rearrange the cards in any order and only the top cards
     * will get picked.
     * @param invcards the investigator card list
     * @param picks the number of cards that will be kept
     */
    public void updateForCardSelection(String title,
            String description, List<InvestigatorCard> invcards, int picks)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Card List");
        invCardListModel.clear();
        cardListChoices = picks;
        for (int i = 0; i < invcards.size(); i++)
        {
            invCardListModel.addElement(invcards.get(i));
        }
        slidingPanel.setExtended(true);
        actionOkButton.setEnabled(true);
        actionCancelButton.setVisible(false);
    }

    public void updateForCardChoice(String title, String description, String[] buttonLabels,
            String[] actionCommands, ActionListener listener, int picks)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(String.valueOf(description));
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Card Choice");
        cardChoiceList.setVisibleRowCount(picks);
        cardChoiceButtonsPane.removeAll();
        cardChoiceButtonsPane.add(Box.createHorizontalGlue());
        for (int i = 0; i < buttonLabels.length; i++)
        {
            if (i != 0)
            {
                cardChoiceButtonsPane.add(Box.createHorizontalStrut(2));
            }
            JButton button = new JButton(buttonLabels[i]);
            button.setBorder(BorderFactory.createEmptyBorder(2, 10, 2, 10));
            button.setActionCommand(actionCommands[i]);
            button.addActionListener(listener);
            cardChoiceButtonsPane.add(button);
        }
        cardChoiceListModel.clear();
        cardChoiceButtonsPane.add(Box.createHorizontalGlue());
        actionOkButton.setEnabled(false);
        slidingPanel.setExtended(true);
        actionCancelButton.setVisible(false);
    }

    /**
     * Presents an interface for adjusting the skill sliders
     * @param inv the investigator to adjust for
     * @param noFocusLimit true if there should not be a limit on focus
     */
    public void updateForSliderAdjustment(Investigator inv, boolean noFocusLimit)
    {
        actionPanelInv = inv;
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText("Adjust Sliders");
        helpString.setText("Use the skill sliders to adjust your skills and press OK when ready.");
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Adjust Sliders");
        inv.setFocus(noFocusLimit ? 10000 : inv.getCurrentFocus());
        actionOkButton.setEnabled(true);
        slidingPanel.setExtended(true);
        actionCancelButton.setVisible(false);
        updateAdjustSliderPanel();
    }

    public void updateForUpkeep(String title, String description, Investigator inv)
    {
        actionPanelInv = inv;
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Adjust Sliders");
        inv.setFocus(inv.getCurrentFocus());
        updateAdjustSliderPanel();
        actionOkButton.setEnabled(true);
        slidingPanel.setExtended(true);
        actionCancelButton.setVisible(false);
    }

    /**
     * Updates the slider panel and writes in the fields
     */
    private void updateAdjustSliderPanel()
    {
        /* Write the skills */
        for (int i = 0; i < 3; i++)
        {
            StyledDocument doc = skillSliderDisplays[i].getStyledDocument();
            skillSliders[i].setValue(actionPanelInv.getSkillSlider(i));
            try
            {
                doc.remove(0, doc.getLength());
                doc.insertString(doc.getLength(),
                        XmlTools.pad(InvestigatorSkill.getSkillName(i * 2), 5) + ": ",
                        doc.getStyle("prestigeBlueBlack"));
                for (int j = 0; j <= 3; j++)
                {
                    doc.insertString(doc.getLength(),
                            String.valueOf(actionPanelInv.getBaseSkill(i * 2) + j) + " ",
                            doc.getStyle("prestigeBlue"));
                }
                doc.insertString(doc.getLength(), "\n",
                        doc.getStyle("prestigeBlueBlack"));
                doc.insertString(doc.getLength(),
                        XmlTools.pad(InvestigatorSkill.getSkillName(i * 2 + 1), 5) + ": ",
                        doc.getStyle("prestigeRedBlack"));
                for (int j = 0; j <= 3; j++)
                {
                    doc.insertString(doc.getLength(),
                            String.valueOf(actionPanelInv.getBaseSkill(i * 2 + 1) - j) + " ",
                            doc.getStyle("prestigeRed"));
                }

            } catch (BadLocationException e)
            {
                e.printStackTrace();
            }
        }
        if (actionPanelInv.getFocus() > 999)
        {
            focusDisplay.setText("Focus: *");
        }
        else
        {
            focusDisplay.setText("Focus: " + actionPanelInv.getFocus());
        }
    }

    /**
     * Updates the action panel for investigator selection
     * @param title the title to display
     * @param description the description to display
     * @param list the list of choices to choose from
     * @param active whether the current player is the active player
     */
    public void updateForTextChoices(String title, String description, List<String> list)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Action List");
        actionListModel.removeAllElements();
        for (String s : list)
        {
            actionListModel.addElement(s);
        }
        // clear action listeners
        ActionListener[] actionListArr = actionList.getActionListeners();
        for (int i = 0; i < actionListArr.length; i++)
        {
            actionList.removeActionListener(actionListArr[i]);
        }
        actionList.setEnabled(true);
        actionOkButton.setEnabled(true);
        slidingPanel.setExtended(true);
        actionCancelButton.setVisible(false);
    }

    /**
     * Updates the action panel for selection of mod effects.
     * @param title The title to display
     * @param description The description to display
     * @param modList The list of mod effects
     */
    public void updateForModEffects(String title, String description, List<ModEffect> modList)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Action List");
        actionListModel.removeAllElements();
        actionListModel.addElement(new String("None"));
        for (ModEffect m : modList)
        {
            actionListModel.addElement(m);
        }
        // clear action listeners and set a listener to display appropriate info for
        // mod-effects
        ActionListener[] actionListArr = actionList.getActionListeners();
        for (int i = 0; i < actionListArr.length; i++)
        {
            actionList.removeActionListener(actionListArr[i]);
        }
        actionList.addActionListener(effectListener);
        actionList.setEnabled(true);
        actionOkButton.setEnabled(true);
        slidingPanel.setExtended(true);
        actionCancelButton.setVisible(false);
    }

    /**
     * Updates the action panel for display of rolls
     * @param title the title to display
     * @param description the description to display
     * @param dice the list of dice
     * @param modList the list of mod effects to display, if any
     * @param hasClues set to true if the player has clues to use
     */
    public void updateForRoll(String title, String description, List<Integer> dice,
            List<ModEffect> modList, boolean hasClues)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Roll");
        rollPanelList.setVisible(true);
        rollEffectButton.setVisible(true);
        rollClueButton.setVisible(true);
        actionPanel.revalidate();
        diceDisplayModel.removeAllElements();
        for (Integer i : dice)
        {
            diceDisplayModel.addElement(i);
        }
        rollPanelListModel.removeAllElements();
        if (modList.isEmpty())
        {
            rollPanelListModel.addElement(new String("No effects available"));
            rollPanelList.setEnabled(false);
            rollEffectButton.setEnabled(false);
        }
        else
        {
            for (ModEffect mod : modList)
            {
                rollPanelListModel.addElement(mod);
            }
            rollPanelList.setEnabled(true);
            rollEffectButton.setEnabled(true);
        }
        rollClueButton.setEnabled(hasClues);
        actionOkButton.setEnabled(true);
        slidingPanel.setExtended(true);
        actionCancelButton.setVisible(false);
    }

    /**
     * Updates the action panel for viewing of other players' rolls
     * @param title the title to display
     * @param description the description to display
     * @param dice the list of dice
     */
    public void updateForRollViewing(String title, String description, List<Integer> dice)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Roll");
        rollPanelList.setVisible(false);
        rollEffectButton.setVisible(false);
        rollClueButton.setVisible(false);
        actionPanel.revalidate();
        diceDisplayModel.removeAllElements();
        for (Integer i : dice)
        {
            diceDisplayModel.addElement(i);
        }
        rollPanelListModel.removeAllElements();
        rollPanelListModel.addElement(new String("None"));
        rollPanelList.setEnabled(false);
        rollEffectButton.setEnabled(false);
        rollClueButton.setEnabled(false);
        actionOkButton.setEnabled(false);
        actionCancelButton.setVisible(false);
    }

    /**
     * Updates the action panel for selection of usable effects.
     * @param title The title to display
     * @param description The description to display
     * @param ueffList The list of mod effects
     */
    public void updateForUsableEffects(String title, String description, List<UsableEffect> ueffList)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Action List");
        actionListModel.removeAllElements();
        actionListModel.addElement(new String("None"));
        for (UsableEffect u : ueffList)
        {
            actionListModel.addElement(u);
        }
        // clear action listeners and set a listener to display appropriate info for
        // mod-effects
        ActionListener[] actionListArr = actionList.getActionListeners();
        for (int i = 0; i < actionListArr.length; i++)
        {
            actionList.removeActionListener(actionListArr[i]);
        }
        actionList.addActionListener(effectListener);
        actionList.setEnabled(true);
        actionOkButton.setEnabled(true);
        slidingPanel.setExtended(true);
        actionCancelButton.setVisible(false);
    }

    /**
     * Updates the action panel for movement commands
     * @param title The title to display
     * @param description The description to display
     * @param inv the investigator undertaking movement
     * @param ueffList a list of usable effects
     * @param tradeTargets a list of investigators to trade with
     */
    public void updateForMovement(String title, String description, Investigator inv,
            List<UsableEffect> ueffList, List<String> tradeTargets)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Movement");
        moveRemainDisplay.setText("Movement Left: " + inv.getMovementPoints());
        movePanelListModel.removeAllElements();
        /* Display movement effects, if any */
        if (inv.areMovementEffectsLocked() || ueffList.isEmpty())
        {
            if (inv.areMovementEffectsLocked())
            {
                movePanelListModel.addElement("Cannot use effects");
            }
            else
            {
                movePanelListModel.addElement("No effects available");
            }
            movePanelList.setEnabled(false);
            moveEffectButton.setEnabled(false);
        }
        else
        {
            for (UsableEffect ueff : ueffList)
            {
                movePanelListModel.addElement(ueff);
            }
            movePanelList.setEnabled(true);
            moveEffectButton.setEnabled(true);
        }
        /* Display trade targets, if any */
        actionListModel.removeAllElements();
        if (tradeTargets.isEmpty())
        {
            actionListModel.addElement("No one");
            tradeRecipientList.setEnabled(false);
            moveTradeButton.setEnabled(false);
        }
        else
        {
            for (String name : tradeTargets)
            {
                actionListModel.addElement(name);
            }
            tradeRecipientList.setEnabled(true);
            moveTradeButton.setEnabled(true);
        }
        actionOkButton.setEnabled(true);
        slidingPanel.setExtended(true);
        actionCancelButton.setVisible(false);
    }

    /**
     * Updates the action panel for trading of items
     * @param title The title to display
     * @param description The description to display
     * @param inv1 the initiating investigator
     * @param inv2 the receiving investigator
     */
    public void updateForTrade(String title, String description, Investigator inv1, Investigator inv2)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        tradeListModel1.clear();
        tradeListModel2.clear();
        inv1.sortInvcards();
        inv2.sortInvcards();
        for (InvestigatorCard card : inv1.getInvcards())
        {
            if (card.isItem())
            {
                tradeListModel1.addElement(card);
            }
        }
        for (InvestigatorCard card : inv2.getInvcards())
        {
            if (card.isItem())
            {
                tradeListModel2.addElement(card);
            }
        }
        actionOkButton.setEnabled(true);
        slidingPanel.setExtended(true);
        actionCancelButton.setVisible(true);
        actionCancelButton.setEnabled(true);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Trade");
    }

    /**
     * Updates the action panel for trading of items
     * @param title The title to display
     * @param description The description to display
     * @param inv1 the initiating investigator
     * @param inv2 the receiving investigator
     * @param list1 the list of items that the initator is trading
     * @param list2 the list of items the initiator wishes to trade for
     */
    public void updateForTradeAccept(String title, String description,
            Investigator inv1, Investigator inv2,
            List<InvestigatorCard> list1, List<InvestigatorCard> list2)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        acceptListModel1.clear();
        acceptListModel2.clear();
        inv1.sortInvcards();
        inv2.sortInvcards();
        for (InvestigatorCard card : inv1.getInvcards())
        {
            if (card.isItem())
            {
                acceptListModel1.addElement(card);
            }
        }
        for (InvestigatorCard card : inv2.getInvcards())
        {
            if (card.isItem())
            {
                acceptListModel2.addElement(card);
            }
        }
        for (InvestigatorCard card : list1)
        {
            acceptListModel1.addCheck(card);
        }
        for (InvestigatorCard card : list2)
        {
            acceptListModel2.addCheck(card);
        }
        acceptListModel1.lockAll();
        acceptListModel2.lockAll();
        actionOkButton.setEnabled(true);
        actionCancelButton.setVisible(true);
        actionCancelButton.setEnabled(true);
        slidingPanel.setExtended(true);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Trade Accept");
    }

    /**
     * Updates the action panel for start of combat
     * @param title The title to display
     * @param description The description to display
     * @param monlist the list of monsters
     */
    public void updateForStartCombat(String title, String description, List<Monster> monlist)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Action List");
        actionListModel.removeAllElements();
        for (Monster m : monlist)
        {
            actionListModel.addElement(m);
        }
        // clear action listeners
        ActionListener[] actionListArr = actionList.getActionListeners();
        for (int i = 0; i < actionListArr.length; i++)
        {
            actionList.removeActionListener(actionListArr[i]);
        }
        actionList.addActionListener(monsterListener);
        actionList.setEnabled(true);
        actionOkButton.setEnabled(true);
        actionCancelButton.setVisible(false);
        slidingPanel.setExtended(true);
    }

    /**
     * Updates the action panel for combat and weapons selection
     * @param title the title to display
     * @param description the description to display
     * @param mon the monster in combat
     * @param equippedList the list of equipped weapons/spells
     * @param handsFree the number of hands free
     * @param weaponList the list of selectable weapons
     * @param numChecked how many of the topmost weapons to start off checked
     * @param effectList the list of usable effects
     * @param evadeActive set to true if the evade button is to be active
     */
    public void updateForCombat(String title, String description, Monster mon,
            List<Object> equippedList, int handsFree, List<EffectSource> weaponList,
            int numChecked, List<ModEffect> effectList, boolean evadeActive)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        monsterNameDisplay.setText(mon.getName());
        combatPanelMon = mon;
        equippedListModel.removeAllElements();
        if (equippedList.isEmpty())
        {
            equippedListModel.addElement(new String("None"));
        }
        else
        {
            for (Object obj : equippedList)
            {
                equippedListModel.addElement(obj);
            }
        }
        this.handsFree = handsFree;
        handsFreeDisplay.setText("Hands Free: " + handsFree);
        weaponsListModel.clear();
        for (int i = 0; i < weaponList.size(); i++)
        {
            EffectSource card = weaponList.get(i);
            weaponsListModel.addElement(card);
            if (i < numChecked)
            {
                weaponsListModel.addCheck(card);
            }
        }
        weaponsList.setEnabled(handsFree != 0);
        combatPanelListModel.removeAllElements();
        if (effectList.isEmpty())
        {
            combatPanelListModel.addElement(new String("None"));
            combatPanelList.setEnabled(false);
            combatEffectButton.setEnabled(false);
        }
        else
        {
            for (ModEffect mod : effectList)
            {
                combatPanelListModel.addElement(mod);
            }
            combatPanelList.setEnabled(true);
            combatEffectButton.setEnabled(true);
        }
        combatEvadeButton.setEnabled(evadeActive);
        actionOkButton.setEnabled(true);
        actionCancelButton.setVisible(false);
        slidingPanel.setExtended(true);
        CardLayout layout = (CardLayout)cardPanel.getLayout();
        layout.show(cardPanel, "Combat");
    }

    /**
     * Updates the action panel for encounter selection.
     * @param title the title to show
     * @param description the description to show
     * @param encounterNames the list of names of the encounters
     * @param encounterDescs the list of descriptions of the encounters
     */
    public void updateForEncounterSelection(String title, String description,
            List<EventNode> encounters)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        actionListModel.removeAllElements();

        encounterList.clear();
        for (int i = 0; i < encounters.size(); i++)
        {
            actionListModel.addElement("Encounter " + (i + 1));
            encounterList.add(encounters.get(i));
        }
        // clear action listeners
        ActionListener[] actionListArr = actionList.getActionListeners();
        for (int i = 0; i < actionListArr.length; i++)
        {
            actionList.removeActionListener(actionListArr[i]);
        }
        actionList.addActionListener(encounterListener);
        actionList.setEnabled(true);
        actionOkButton.setEnabled(true);
        slidingPanel.setExtended(true);
        actionCancelButton.setVisible(false);
    }

    /**
     * Updates the action panel for other world encounter selection.
     * @param title the title to show
     * @param description the description to show
     * @param cards the list of gate cards
     * @param owLocation the other world location
     */
    public void updateForOWEncounterSelection(String title, String description,
            List<GateCard> cards, String owLocation)
    {
        String curPlayer = gameData.getCurrentPlayerName();
        int turn = gameData.getCurrentPlayer() + 1;
        actionTitle.setText(title);
        helpString.setText(description);
        activePlayerDisplay.setText("Turn: (" + turn + ") " + curPlayer);
        actionListModel.removeAllElements();
        this.owLocation = owLocation;

        for (int i = 0; i < cards.size(); i++)
        {
            actionListModel.addElement(cards.get(i));
        }
        // clear action listeners
        ActionListener[] actionListArr = actionList.getActionListeners();
        for (int i = 0; i < actionListArr.length; i++)
        {
            actionList.removeActionListener(actionListArr[i]);
        }
        actionList.addActionListener(gateCardListener);
        actionList.setEnabled(true);
        actionOkButton.setEnabled(true);
        slidingPanel.setExtended(true);
        actionCancelButton.setVisible(false);
    }
}
